Update: This patch is pretty outdated. There have been major rewrites in mod_gnutls since then. I’m not sure whether the current version properly supports
subjectAltNames; I don’t use mod_gnutls myself anymore.
Yesterday, I wrote a small patch against mod_gnutls (that’s the GNU alternative to mod_ssl, and it’s leaner; and it supports SNI (server name indication), whereas even the version of mod_ssl in the upcoming Debian squeeze release doesn’t). It took me quite a while to figure out the problem in the first place, and I guess it’s a bit of a corner case, but I can’t imagine I’m the only person who might run into this problem, so here is an explanation.
Server Name Indication: one IP address, many certificates
Suppose you run a web server that has one IP address (and you don’t really need to waste more addresses for a simple web server, right?) but that hosts websites 0n several different domains. In the distant past, you were out of luck: only one SSL certificate per IP address, for technical reasons.
Along came SNI. It’s a little extension that allows your client (i.e. browser) to tell the TLS layer in your web browser the hostname you are connecting to before the certificates are even exchanged. So, this allows your server to select a certificate from a big bunch of different ones, based on the names found in the certificates.
Small reminder: certificates and names
When your client requests an SSL-encrypted website, the certificate is bound to the hostname by way of the “CN” (Common Name) field. If the hostname in the URL matches the CN of the certificate, the certificate is used. Otherwise you get a warning (and in recent browsers it’s a very, very discouraging warning).
Now, that’s inconvenient, because sometimes you might want to use the same certificate for two or three different hostnames, e.g. example.org and www.example.org at the same time. Setting aside solutions involving wildcard names, eventually an extension was developed for specifying alternative names in the certificate. With that, you can simply set one of your hostnames as the CN and all the others as alternative names, and the certificate can then be used by the client for all of those names.
StartSSL Level 1 certificates and alternative names
I’m mentioning StartSSL here specifically because they offer something pretty unique: free SSL certificates that are accepted by most of the standard browsers.
Of course, they don’t offer you maximum flexibility for free, which is perfectly fine as far as I’m concerned, especially since the certificates you do get are, in theory, very useful already. They rid you of SSL warnings on your domains, and for basic applications you don’t really need more than that.
Each of these level 1 (=free) certificates contains exactly one subdomain of a domain you own, and the domain name itself as an alternative name. For example, this might give you CN=www.example.org and subjectAltName=example.org.
So what’s the big problem? Nothing, usually, but with a certain combination of requirements it’s basically impossible to set up (but wait for my solution).
Now, suppose I’ve got two domains, example.org and org.example. Cute, aren’t they? Let’s also say that both are hosted on the same server and even on the same IP address. Finally, let’s say that I want the pages served as http://example.org/ and http://org.example/, i.e. without a www prefix.
First off, note that mod_ssl, the standard solution for SSL with Apache (in turn the standard solution for serving web pages on Linux and friends), doesn’t support SNI (which is what we need for all of this to work at all) before OpenSSL version 1.0.0. And, well, the venerable Debian will apparently include an older version of OpenSSL in their upcoming release, so that SNI with mod_ssl is basically a no-go on Debian stable servers for the next couple of years.
So, if you want to stick with Apache, the most important contender (in fact the only one I know) is mod_gnutls, based on GnuTLS instead of OpenSSL. mod_gnutls is a fairly minimalistic implementation, and it has worked very well for me in the last year or two.
However, one of its ways of keeping things simple causes problems with the scenario I outlined above.** mod_gnutls only matches against the first name in each certificate.** With that in mind, let’s go back to my two websites with their corresponding (hypothetical) StartSSL level 1 certificates:
- CN=www.example.org, subjectAltName=example.org
- CN=www.org.example, subjectAltName=org.example
So, suppose I configure the web server to use these certificates and then I try to open https://org.example/ in my web browser. This is what happens:
- My browser opens a connection to the web server, initiates a TLS connection and tells mod_gnutls that I’m requesting from “org.example”.
- mod_gnutls gets a name from the first certificate. Aha, it has a CN, let’s use that. Does “org.example” match “www.example.org”? Nope, it doesn’t, so mod_gnutls forgets about that certificate.
- mod_gnutls gets a name from the second certificate. Aha, it has a CN, let’s use that. Does “org.example” match “www.org.example”? Nope, it doesn’t, so mod_gnutls forgets about that certificate.
- All right, we haven’t found a matching certificate, so mod_gnutls is just going to use the first one in the list.
- My browser gives me a warning: “the certificate’s name doesn’t match. You are visiting org.example but the certificate was for www.example.org.”
I already tried StartSSL certificates some time ago but found that I couldn’t use them due to this kind of scenario. Just a few days ago I looked into it again and finally decided to just write a small patch for mod_gnutls.
This patch is not a work of art. It simply extends the number of names mod_gnutls uses from each certificate. With the patch added, mod_gnutls obtains up to four names from the certificate (CN first, then subjectAltNames) and matches each of them against the SNI value. This effectively eliminates the problem described above.
The downside is that it’s not very efficient. Right now, for every request, mod_gnutls has to re-extract the names from the certificates and match against all of them. This is, of course, the main reason I decided to limit the number of names used. A better implementation might read all certificates during start-up, extract the names and provide a lookup table, with a bit of added magic to deal with wildcard names (e.g. *.example.org).
My setup doesn’t warrant this effort. I have fixed my problem, and perhaps my fix is sufficient for you, too.