Virtual Hosting and SSL

All right, now that we have some blogs up. We will want to enable SSL/TLS security, also known as https. The reason you want this may be obvious to some. But to spell it out for you, if you don’t use https to connect to your administrative pages in Movable Type while you’re sipping on your latte, then everyone else that’s on that wireless network can see you type your password in, plain as day. And by “can”, I mean there is nothing preventing them from watching your traffic. Most likely, no one is, but you never know.

There are a couple of complications that we will need to work through though. Firstly, SSL and name based virtual hosting are mostly incompatible. And secondly, unless you get a Certificate Authority, such as Verisign, to sign your SSL key you and your security conscious visitors will be presented with an ugly message from the browser saying that your site is trying to identify itself with an invalid security certificate.

So, why are SSL and name based virtual hosting mostly incompatible? Well, it turns out that the way name based virtual hosting works is for the browser to send to your web server the name of the server it’s trying to connect to. And then your web server uses that information to look through the list of virtually hosted domains until it finds a match. Then the page the browser wants is sent from the server to the browser. But if you have connected to the web server using an https connection then the communication channel needs to be encrypted. So the server needs to send the browser the public key of the domain the browser is trying to connect to. But the server doesn’t yet know what domain the browser is trying to connect to, because that information is part of what will eventually be encrypted and sent by the browser. Apache will just serve the first certificate it finds in this case. So all but the first domain in your list of virtually hosted domains will cause the browser to issue an additional warning, that the certificate is for the wrong domain. This isn’t a big problem for large hosting companies or business, they can just assign a separate IP address to each domain. And then use that extra information to configure the web server, allowing it to pass the correct domain specific key back to the browser. For us little guys, that’s not really a suitable approach. You can do something similar by having your web server listen on a bunch of different ports, one for each domains SSL connection. But then your URLs will have to have the port number in them as well. Not a really classy solution. There is a solution to this problem in the works. It’s called “Server Name Indication” or SNI, and it’s part of the TLS protocol. Unfortunately it’s still not readily available (see this page). So, what’s the solution? It’s pretty simple actually, just use one certificate for all of your domains. There is an extension that allows for multiple domain names to be associated with a single certificate. When a browser receives such a certificate, it looks at all of the domain names and if any of them match it is satisfied. There seem to be some security issues with this feature (see this page). But since we are mainly interested in using it to authenticate ourselves with our own server, it’s not a big deal, I think. And that brings us to our second issue. We can pay Verisign to generate a certificate for us, or we can sign it ourself. If you pay Verisign (or other Certificate Authority) then anyone that browses your web site with a secure https connection will feel right at home, secure even. If you sign the certificate yourself, then viewers will be presented with a nastygram from their browser. Since I am mainly interested in being able to securely access my servers from insecure networks, I’m happy to sign the certificates myself.

First you need to enable SSL in your Apache configuration. In the Apache2 configuration for Ubuntu these files are located in /etc/apache2. You’ll need to make sure that your ports.conf file contains something like the following:

Listen 80

<IfModule mod_ssl.c>
    Listen 443
</IfModule>

This causes Apache to listen on port 443 for connections as well as port 80. Port 443 is the https port. And your conf.d/namevirtualhosts file should look something like:

NameVirtualHost *:80
NameVirtualHost *:443

This tells Apache to look up virtual hosts by name for traffic coming in on either port. Next you need to make sure that your conf.d/ssl_certificate file looks something like:

SSLCertificateFile    /etc/apache2/ssl/serverwide.crt
SSLCertificateKeyFile /etc/apache2/ssl/serverwide.key

You can see that I’ve called my certificate and key files serverwide to make it obvious that they are used by all domains served by this server. It is very important that these files have their permissions set so that only root can read them. You’ll also need to make symlinks from the ssl.conf and ssl.load files in your mods-available directory to your mods-enabled directory.

Now we’re ready to generate and sign our key and certificate. Once you have generated the certificate you can inspect it’s contents with this command.

openssl x509 -in serverwide.crt -noout -text

To generate a key use the following command. You will be asked for a pass phrase. It is important that you remember this pass phrase or your key will be lost to you forever. Or at least until computers are powerful enough to brute force crack your key.

openssl genrsa -des3 -rand /dev/urandom -out serverwide.key 1024

Once you have a key you can create and sign a certificate with the following command. You will be asked for the pass phrase you entered above. This is because the key is protected by that pass phrase and can’t be used without it. This certificate will be valid for one year.

openssl req -config server.config -new -key serverwide.key -out serverwide.crt -x509 -days 365

Most of the options we need to pass to OpenSSL to create and sign the certificate can be passed in a configuration file. The command line above assumes the configuration file is called server.config. Below is an example server.config file, the main lines of interest are in the alt_names section. The alt_names section is where you can put all of the virtually hosted domains on your server. The browser will look for a match with any of those domains when the server passes it the certificate we have just generated. I also found that subjectAltName had to be in both the v3_req and v3_ca sections.

[ req ]
default_bits       = 1024
default_md         = sha1
distinguished_name = req_distinguished_name
prompt             = no
string_mask        = nombstr
req_extensions     = v3_req
x509_extensions    = v3_ca

[ req_distinguished_name ]
countryName            = &lt;country code&gt;
stateOrProvinceName    = &lt;state&gt;
localityName           = &lt;city&gt;
organizationName       = &lt;whatever, could be your name&gt;
organizationalUnitName = &lt;again, whatever&gt;
commonName             = www.domain1.com
emailAddress           = webmaster@domain1.com

[ v3_req ]
basicConstraints       = CA:FALSE
keyUsage               = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName         = @alt_names

[ v3_ca ]
subjectAltName         = @alt_names

[ alt_names ]
DNS.1 = www.domain1.com
DNS.2 = www.domain2.com
DNS.3 = www.domain3.com

The key that we have generated will have a pass phrase associated with it (the one you entered when you generated the key). This pass phrase will need to be entered every time you restart your apache server. There are ways of removing this extra security, but if you want to do that I’ll let you look that one up elsewhere. For me with a server running on a UPS, I reboot or restart apache a couple of times a year at most. The added security is well woth it. If your server get’s compromised, and it will eventually, then your security key will be coppied. And that is as they say, a bad thing.

And finally, in your site configuration file, probably in /etc/apache2/sites-available, you will need to add the following:

<VirtualHost *:443>
    ServerName   www.domain1.com
    ServerAlias  domain1.com *.domain1.com
    DocumentRoot /var/www/domain1

    SSLEngine    On
</VirtualHost>

This is the Virtual host configuration file for your secured site. You’ll want to add any additional configurations to it from your normal *:80 configuration section.