Traditionally, the SSL protocol hasn't allowed the implementation of name-based virtual hosting with Apache. The problem is quite simple: for SSL (or TLS, which is what it is nowadays usually), Apache should present an SSL certificate to the client; if there are many SSL virtual hosts, Apache needs to know which one was requested by the client (to select the proper certificate); to do that, it reads the HOST header sent by the client in the HTTP dialog. But the SSL session should be established before the HTTP dialog even begins, so we have a chicken-and-egg problem here.
There are a few ways around this problem. Each one has issues. We'll have a look at each of these here.
HTTP over TLS is defined by the informational RFC 2818. That RFC contains an important part regarding the methods used for endpoint identification, ie how the client and the server identify each other. The important part for the discussion about virtual hosts is how the server identifies to the client. This is what it says: "If a subjectAltName extension of type dNSName is present, that MUST be used as the identity. Otherwise, the (most specific) Common Name field in the Subject field of the certificate MUST be used. Although the use of the Common Name is existing practice, it is deprecated and Certification Authorities are encouraged to use the dNSName instead". This is very important: despite the overwhelming majority of websites still using the CN (Common Name), that is deprecated, and subjectAltName should be used instead. In practice, most CA (Certification Authorities) nowadays put the information in both places. However, it is valid to only use subjectAltName.
We will refer to the RFC when appropriate in the rest of the post.
This has probably been one of the first tricks people used to get around the limitations of SSL. Essentially, just put multiple Common Names (CN) in the certificate's subject, ie like
- As we saw, the use of CN is deprecated. Nonetheless, it still seems to be used by the majority of sites out there.
- Not all browsers support multiple CNs; some (like Firefox) will just pick the first one as the server's name, and if the connection happens to be to one of the other names, a name mismatch warning is issued.
- From what I can see, not all the CAs will sign a certificate with multiple CN (but probably for mere money reasons, not for technical reasons).
- All the CNs must be listed and thus exposed to whoever happens to inspect the certificate. This might not be desirable, especially if you discover that www.yourbank.com is listed along with www.p0rnwarez.com.
Another widely used trick is to use a wildcard certificate, ie a certificate that covers any name ending in .example.com (for example). In other words, a certificate for *.example.com.
Some considerations on wildcard cetificates:
- Again, the wildcard string is (usually) listed in the CN. That is deprecated, but still the majority of sites use it. Putting the wildcard string in a subjectAltName would probably be more appropriate. With some random sample of Internet sites using wildcards, it seems that some use only CN, some use both CN and subjectAltName (perhaps with some additional name listed in the subjectAltName, like eg *.example.com, www.example.com, example.com).
- According to the RFC, all the names covered by the wildcard certificate must be at the first level under the domain, so for example www.example.com and dev.example.com are ok, but www.sales.example.com is not. This is the official version: "Names may contain the wildcard character * which is considered to match any single domain name component or component fragment. E.g., *.a.com matches foo.a.com but not bar.foo.a.com. f*.com matches foo.com but not bar.com.". See also this page for a more detailed discussion.
- Obviously, with a single wildcard you're restricted to having all the virtual hosts under the same domain. However, this can probably be combined with subjectAltName (see below), so adding names or wildcards in other domain can be possible.
- A wildcard certificate usually costs a lot. If you need it for three or four names, it may be that buying three or four individual certificates is cheaper. Furthermore, CAs usually have licences on the certificates that restrict their use. Typically, the licence allows to use one certificate on a single server. To use it on more servers (which is a likely reason one might want a wildcard certificate in the first place!), additional licences must be purchased. Again, these licences are not free (rather, they usually cost as much as the certificate).
Subject Alternative Name (SAN)
Subject Alternative Name (or subjectAltName, or SAN for short) is one of a number of X.509v3 extensions that can be included in a certificate. It allows to specify additional information like email addresses, IP numbers, and (more interesting) DNS names. As we saw, the use of subjectAltName is the preferred way the client must use for server identification, and thus it should be used in preference to the CN (though as I said, in practice the use of CN still seems to be prevalent; some CAs put the same names as CN and SANs, just in case). Here's a certificate that includes SANs:
Some considerations on SANs:
- SAN certificates are usually more expensive than normal certificates (though surely cheaper than wildcard certificates). Usually, CAs charge per-SAN, so if you're going to add a lot of names, you should consider that. And the same licensing issues (usually) apply as well.
- As with the multiple CN case, exposing all the names cannot be avoided.
- Since the RFC says that if a subjectAltName is present that MUST be used to identify the server, putting a name in the CN and other names as SAN might not work with strictly RFC-compliant browsers (like Firefox), in that they might accept only the names listed as SANs; so you better put all the names as SANs, regardless of whether or how many you put in the CN.
- It's definitely possible, and some certificates used by some websites confirm that, to use one or more wildcard names like *.example.com as SANs; this will probably cost quite a lot of money, but the sites that can do that can probably afford it.
- Once a certificate containing SANs is issued, it's not possible to remove or add names; a new certificate must be issued.
Apache configuration for the previous methods
Configuring Apache to support multiple SSL virtual hosts using one of the previous techniques is quite simple. Since only one certificate is involved, just configure each virtual host to use the same certificate:
Listen 80 # if you do non-SSL virtual hosts NameVirtualHost *:80 <IfModule mod_ssl.c> Listen 443 # Yes, this shouldn't be here, but ... NameVirtualHost *:443 </IfModule> ... <IfModule mod_ssl.c> <VirtualHost *:443> DocumentRoot /var/www/www.example.com ServerName www.example.com ErrorLog /var/log/apache2/www.example.com-error.log CustomLog /var/log/apache2/www.example.com-ssl_access.log combined SSLEngine on SSLCertificateFile /etc/ssl/certs/yourcert.pem SSLCertificateKeyFile /etc/ssl/private/yourkey.key </VirtualHost> <VirtualHost *:443> DocumentRoot /var/www/dev.example.org ServerName dev.example.org ErrorLog /var/log/apache2/dev.example.org-error.log CustomLog /var/log/apache2/dev.example.org-ssl_access.log combined SSLEngine on SSLCertificateFile /etc/ssl/certs/yourcert.pem SSLCertificateKeyFile /etc/ssl/private/yourkey.key </VirtualHost> ... # other virtual hosts here .... </IfModule>
Where, of course, /etc/ssl/certs/yourcert.pem will be your wildcard/SAN/whatever certificate (in this case, a SAN certificate, since the domains are different).
In practice, Apache will probably use the certificate defined in the virtual host that comes first in the configuration, but it doesn't hurt to be explicit even if it's redundant. UPDATE: removing all the SSL directives from all the virtual hosts but the first seems indeed to work fine. I still prefer to be expicit though.
Note that apache will surely complain at startup, eg something like this:
[Wed Dec 15 22:32:46 2009] [warn] Init: SSL server IP/port conflict: dev.example.org:443 (/etc/apache2/sites-enabled/dev.example.org:2) vs. www.example.com:443 (/etc/apache2/sites-enabled/www.example.com:2) [Wed Dec 15 22:32:46 2009] [warn] Init: You should not use name-based virtual hosts in conjunction with SSL!!
but then it will work fine, since ultimately the certificate that it will serve to clients will always be the same, and Apache's name-based virtual host selection will still work (to get ServerName, DocumentRoot, etc.) once the SSL connection has been established.
Server Name Indication
All the previous techniques are somehow kludgy (except maybe the SAN), because they still require that everything is done with a single certificate (and thus a single responsible organization). This is not very flexible, and it is also impractical when a web server hosts hundreds or thousands of SSL websites; it would be nice to be able to have truly distinct certificates, one per virtual host. Here's where Server Name Indication (or SNI for short) comes into play. SNI is an extension to the TLS protocol (see the RFC), that allows the client to send the requested server name in cleartext at the beginning, as part of the initial SSL/TLS negotiation (in the ClientHello message, in case you're curious). This way, the server can learn which site the client is requesting, and serve the appropriate certificate. This is by far the cleanest way to do SSL virtual hosting, since it allows to have many different certificates, each of which could potentially be managed by a different organization or individual.
To work, SNI must be supported by both the client (because it sends the server's name when it starts the SSL negitiation) and the server (because it must know how to look for the requested name in the incoming request and present the right certificate for it). Fortunately, these days the majority of the most popular browsers support SNI, and recent versions of OpenSSL (since 0.9.8f) and Apache (since 2.2.12, finally) support it as well.
To do SNI, Apache is configured exactly in the same way as before, except a different certificate (and key) is specified for each virtual host:
Listen 80 # if you do non-SSL virtual hosts NameVirtualHost *:80 <IfModule mod_ssl.c> Listen 443 # Now this has the full right to be here ... NameVirtualHost *:443 </IfModule> ... <IfModule mod_ssl.c> <VirtualHost *:443> DocumentRoot /var/www/www.example.com ServerName www.example.com ErrorLog /var/log/apache2/www.example.com-error.log CustomLog /var/log/apache2/www.example.com-ssl_access.log combined SSLEngine on SSLCertificateFile /etc/ssl/certs/www.example.com-cert.pem SSLCertificateKeyFile /etc/ssl/private/www.example.com-key.key </VirtualHost> <VirtualHost *:443> DocumentRoot /var/www/dev.example.org ServerName dev.example.org ErrorLog /var/log/apache2/dev.example.org-error.log CustomLog /var/log/apache2/dev.example.org-ssl_access.log combined SSLEngine on SSLCertificateFile /etc/ssl/certs/dev.example.org-cert.pem SSLCertificateKeyFile /etc/ssl/private/dev.example.org-key.key </VirtualHost> ... # other virtual hosts here .... </IfModule>
And when Apache starts up you should see this (more reassuring) message in the log:
[Thu Dec 16 21:16:19 2009] [warn] Init: Name-based SSL virtual hosts only work for clients with TLS server name indication support (RFC 4366)
This is how a Client Hello as sent by Firefox looks like, including the SNI:
Some considerations about SNI:
- While support is improving, it might not be widespread yet; in particular, it seems that Internet Explorer supports it only in IE7, and only from Vista; there are still a lot of IE6 and Windows XPs out there. Then, SNI is a TLS extension (and maybe SSLv3); surely there are still a lot of clients that only do SSLv2, what about them?
- On the server side, it's unusual to be running the latest and greatest software in production setups; this applies to Apache as well, of course. Apache prior to 2.2.12 can do SNI but it needs to use mod_gnutls rather than mod_ssl, which introduces all the usual GnuTLS annoyances.
- If possible, go with SNI over any other option; it's definitely the right way to do SSL virtual hosting.
See this page on the Apache wiki for more information about SNI.
See this page on the CAcert wiki for a good discussion of SSL virtual hosting, and some considerations about the deprecated CN usage.