1/15/14 Update: Fr0X from the Rackspace Cloud Community solved the issue of Browser and OS limitations by leveraging CloudFlare with this solution. His post is at: https://community.rackspace.com/products/f/25/t/1829
Using SNI to host multiple SSL Sites on a single IP in apache using Rackspace Cloud
Common hosting knowledge has always been that if you want to host multiple SSL Sites on a single server you need to assign each website it’s own unique IP address. This makes sense. The whole purpose of SSL is that the request and response are encrypted. So when the request gets to Apache, Apache can not use standard name based hosting because it can not read the name of the site being requested since it is encrypted. To get around this, you can put sites on separate IP addresses and Apache will look at the request and say “I don’t know what site you are requesting, but I know you are requesting it on X IP address, so I will send you to the default site I have for X IP address.”
In a world with unlimited IP addresses this works just fine. The problem is that the world is quickly running out of IPv4 Addresses and that we might be stuck limping around on IPv4 for awhile waiting for ISPs to catch up with IPv6.
Enter SNI (Server Name Indication). SNI allows for browsers to send the hostname (domain) being requested separately un-encrypted so that the web server can understand the request and serve the right virtual host. It is not without it’s drawbacks though, let’s look at what those are:
Server Pre-Requisites
You must be running Apache 2.12 or higher, and you must be running openSSL 0.9.8f or higher. RHEL/CentOS 5.5 do not have both of these version available in the standard repositories or the extended EPEL repos, so yum install is out the window on those Distros. You will be stuck building from source. This is a game changer since your package manager is no longer aware of the installation of that software and will cause all sorts of headaches. Your options would be to search for a repo that does include these later versions and install it (I haven’t looked too hard yet), install from source, pray that 5.6 has it and wait, or go with Fedora 14.
For this example, I am going to go with Fedora 14 because it is the easiest way to demonstrate SNI since the necessary versions are in the yum repos.
Browser Limitations
Oh yeah, you don’t just have to worry about your server. You also have to worry about your user’s browsers. Not all of them support SNI, but most do. The following browsers work:
- Mozilla Firefox V2 and up.
- Chrome
- Opera 8.0 or higher
- IE 7 or higher on Vista or higher. (Sorry, IE 7 on XP won’t work)
- Safari 3.2.1 on OS X 10.5.6 or higher
Ok, so that’s all the bad news. Is that enough to scare you away from it? Maybe. Only you can decide that, and as IPv4 addresses become more scarce and supply and demand kicks in prices for IPv4 addresses will go up. Only you can determine if this is the right solution for your business and website. Now let’s dive into a server!
Proof of Concept
Let’s see this in action! I am going to do these steps using a Fedora 14 Rackspace Cloud Server. I will do this using self-signed certs, and it will be a minimum install because it is simply proof of concept. I will be using domains test1.com and test2.com and modifying my hosts file to point to the IP of my server. Start up the server and login as root.
Install what you will need
1 |
yum install openssl httpd mod_ssl |
Add in Firewall Rules
1 2 |
iptables -I INPUT -p tcp --dport 80 -j ACCEPT iptables -I INPUT -p tcp --dport 443 -j ACCEPT |
Create Some Directories
1 2 3 4 5 6 |
mkdir -p /etc/httpd/certs/test1.com mkdir -p /etc/httpd/certs/test2.com mkdir -p /var/www/vhosts/test1/html mkdir -p /var/www/vhosts/test2/html mkdir -p /var/www/vhosts/test1/logs mkdir -p /var/www/vhosts/test2/logs |
Create Some Index Files
Put something in them so we can see if it works
1 2 |
echo "This is test 1" > /var/www/vhosts/test1/html/index.html echo "This is test 2" > /var/www/vhosts/test2/html/index.html |
Create the self-signed Certs
Again, I am using test1.com and test2.com. Several of these commands will prompt you for input, just roll with it.
1 2 3 4 5 |
cd /etc/httpd/certs/test1 openssl genrsa -des3 -out server.key 4096 openssl req -new -key server.key -out server.csr openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt openssl rsa -in server.key -out server.key.insecure |
Repeat those steps above for a self signed cert for test2.com
Edit your Apache Config File
add into /etc/httpd/conf/httpd.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
NameVirtualHost *:80 NameVirtualHost *:443 SSLStrictSNIVHostCheck off <VirtualHost *:80> ServerAdmin someone@somewhere.com DocumentRoot /var/www/vhosts/test1/html ServerName test1.com ErrorLog /var/www/vhosts/test1/logs/error.log CustomLog /var/www/vhosts/test1/logs/access.log common </VirtualHost> <VirtualHost *:80> ServerAdmin someone@somewhere.com DocumentRoot /var/www/vhosts/test2/html ServerName test2.com ErrorLog /var/www/vhosts/test2/logs/error.log CustomLog /var/www/vhosts/test2/logs/access.log common </VirtualHost> <VirtualHost *:443> ServerAdmin someone@somewhere.com DocumentRoot /var/www/vhosts/test1/html ServerName test1.com SSLEngine ON SSLCertificateFile /etc/httpd/certs/test1.com/server.crt SSLCertificateKeyFile /etc/httpd/certs/test1.com/server.key ErrorLog /var/www/vhosts/test1/logs/error.log CustomLog /var/www/vhosts/test1/logs/access.log common </VirtualHost> <VirtualHost *:443> ServerAdmin someone@somewhere.com DocumentRoot /var/www/vhosts/test2/html ServerName test2.com SSLEngine ON SSLCertificateFile /etc/httpd/certs/test2.com/server.crt SSLCertificateKeyFile /etc/httpd/certs/test2.com/server.key ErrorLog /var/www/vhosts/test2/logs/error.log CustomLog /var/www/vhosts/test2/logs/access.log common </VirtualHost> |
Start Apache
1 |
service httpd start |
That’s it – after you edit your local hosts file you should be able to go to https://test1.com and https://test2.com in a browser and see your 2 test files. Note that you WILL get SSL Errors in your browser with the above, but that is only because they are self signed certs. If you look at the error, you will see that it is NOT due to a host name mismatch, but because the signer is not trusted. If you actually buy the certs you won’t get an error.
Leave me a comment and let me know what you think!
According to: http://en.wikipedia.org/wiki/Server_Name_Indication
The following combinations do not support SNI:
Client side
—————————————
Konqueror/KDE in any version[19]
Internet Explorer (any version) on Windows XP
Safari on Windows XP
wget[20]
BlackBerry Browser
Windows Mobile up to 6.5[21]
Android default browser[22] (Targeted for Honeycomb but won’t be fixed until next version for phone users as Honeycomb will be reserved to tablets only)
Oracle Java JSSE (As of 2011[update])
Server side
———————–
Microsoft Internet Information Server IIS (As of 2009[update]).
Apache Tomcat (As of 2011[update])
IBM HTTP Server [23]
Libraries
—————————————-
Qt Client Side up to 4.7[18]
Mozilla NSS server side [24]
Python 2.x (ssl, urllib[2] and httplib modules) [17]
Is this possible if you want to use a Load Balancer to distribute traffic to multiple web servers? I think Rackspace doesn’t support SSL Termination yet. Is there another way?
I would have one small clarification to make:
I see that you remove the pass phrase from the private key but then you do not use the “insecure” private key for Apache (this means that each time Apache starts it will expect the pass phrase to be typed in – maybe that’s what you want…).
However, to user the private key without the pass-phrase I would suggest to more commands in the each directory from /etc/httpd/certs/:
rm -rf server.key
mv server.key.insecure server.key
Thanks,
Adi
Feedback “Create the self-signed Certs” CLI is currently
cd /etc/httpd/certs/test1
it needs to be
cd /etc/httpd/certs/test1.com
It would also make it simple if you include the local hosts file edits