Multiple SSL domains on the same IP address and same port?
Solution 1:
For the most up-to-date information on Apache and SNI, including additional HTTP-Specific RFCs, please refer to the Apache Wiki
FYsI: "Multiple (different) SSL certificates on one IP" is brought to you by the magic of TLS Upgrading. It works with newer Apache servers (2.2.x) and reasonably recent browsers (don't know versions off the top of my head).
RFC 2817 (upgrading to TLS within HTTP/1.1) has the gory details, but basically it works for a lot of people (if not the majority).
You can reproduce the old funky behavior with openssl's s_client
command (or any "old enough" browser) though.
Edit to add: apparently curl
can show you what's happening here better than openssl:
SSLv3
mikeg@flexo% curl -v -v -v -3 https://www.yummyskin.com
* About to connect() to www.yummyskin.com port 443 (#0)
* Trying 69.164.214.79... connected
* Connected to www.yummyskin.com (69.164.214.79) port 443 (#0)
* successfully set certificate verify locations:
* CAfile: /usr/local/share/certs/ca-root-nss.crt
CApath: none
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server key exchange (12):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using DHE-RSA-AES256-SHA
* Server certificate:
* subject: serialNumber=wq8O9mhOSp9fY9JcmaJUrFNWWrANURzJ; C=CA;
O=staging.bossystem.org; OU=GT07932874;
OU=See www.rapidssl.com/resources/cps (c)10;
OU=Domain Control Validated - RapidSSL(R);
CN=staging.bossystem.org
* start date: 2010-02-03 18:53:53 GMT
* expire date: 2011-02-06 13:21:08 GMT
* SSL: certificate subject name 'staging.bossystem.org'
does not match target host name 'www.yummyskin.com'
* Closing connection #0
* SSLv3, TLS alert, Client hello (1):
curl: (51) SSL: certificate subject name 'staging.bossystem.org'
does not match target host name 'www.yummyskin.com'
TLSv1
mikeg@flexo% curl -v -v -v -1 https://www.yummyskin.com
* About to connect() to www.yummyskin.com port 443 (#0)
* Trying 69.164.214.79... connected
* Connected to www.yummyskin.com (69.164.214.79) port 443 (#0)
* successfully set certificate verify locations:
* CAfile: /usr/local/share/certs/ca-root-nss.crt
CApath: none
* SSLv3, TLS handshake, Client hello (1):
* SSLv3, TLS handshake, Server hello (2):
* SSLv3, TLS handshake, CERT (11):
* SSLv3, TLS handshake, Server key exchange (12):
* SSLv3, TLS handshake, Server finished (14):
* SSLv3, TLS handshake, Client key exchange (16):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSLv3, TLS change cipher, Client hello (1):
* SSLv3, TLS handshake, Finished (20):
* SSL connection using DHE-RSA-AES256-SHA
* Server certificate:
* subject: C=CA; O=www.yummyskin.com; OU=GT13670640;
OU=See www.rapidssl.com/resources/cps (c)09;
OU=Domain Control Validated - RapidSSL(R);
CN=www.yummyskin.com
* start date: 2009-04-24 15:48:15 GMT
* expire date: 2010-04-25 15:48:15 GMT
* common name: www.yummyskin.com (matched)
* issuer: C=US; O=Equifax Secure Inc.; CN=Equifax Secure Global eBusiness CA-1
* SSL certificate verify ok.
Solution 2:
Yes, but there are some caveats.
This is accomplished through Server Name Indication, an extension to Transport Layer Security.
What is Server Name Indication?
Server Name Indication (RFC 6066; obsoleted RFC 4366, RFC 3546) is an extension to Transport Layer Security which allows the client to tell the server the name of the host it is trying to reach.
SNI is compatible with TLS 1.0 and higher according to spec, but implementations may vary (see below). It cannot be used with SSL, so a connection must negotiate TLS (see RFC 4346 appendix E) for SNI to be used. This generally happens automatically with supported software.
Why is SNI needed?
In a normal HTTP connection, the browser informs the server of the hostname of the server it is trying to reach using the Host:
header. This allows for a web server on a single IP address to serve content for multiple hostnames, which is commonly known as name-based virtual hosting.
The alternative is to assign unique IP addresses for each web hostname to be served. This was commonly done in the very early days of the web, before it was widely known that IP addresses would run out and conservation measures began, and is still done this way for SSL virtual hosts (not using SNI).
Because this method of transmitting the host name requires the connection to be already established, it does not work with SSL/TLS connections. By the time the secure connection is set up, the web server must already know which hostname it is going to serve to the client, because the web server itself is setting up the secure connection.
SNI solves this problem by having the client transmit the hostname as part of the TLS negotiation, so that the server is already aware of which virtual host should be used to service the connection. The server can then use the certificate and configuration for the correct virtual host.
Why not use different IP addresses?
The HTTP Host:
header was defined to allow more than one Web host to be served from a single IP address due to the shortage of IPv4 addresses, recognized as a problem as early as the mid-1990s. In shared web hosting environments, hundreds of unique, unrelated Web sites can be served using a single IP address this way, conserving address space.
Shared hosting environments then found that the largest consumer of IP address space was the need for secure web sites to have unique IP addresses, creating the need for SNI as a stop-gap measure on the way to IPv6. Today it is sometimes difficult to obtain as few as 5 IP addresses (/29) without significant justification, often resulting in deployment delays.
With the advent of IPv6, such address conservation techniques are no longer necessary, since a single host can have more IPv6 addresses assigned to it than the entire Internet contains today, but the techniques will probably still be used far into the future to service legacy IPv4 connections.
Caveats
Some operating system/browser combinations do not support SNI (see below), so using SNI is not appropriate for all situations. Sites targeting such system/browser combinations would have to forgo SNI and continue to use unique IP addresses for each virtual host.
Of particular note, no version of Internet Explorer on Windows XP supports SNI. As this combination still represents a significant (but steadily decreasing; about 16% of Internet traffic in December 2012 according to NetMarketShare) portion of Internet traffic, SNI would be inappropriate for a site targeting these user populations.
Support
Many, but not all, commonly used software packages support SNI.
(Omission from this list doesn't necessarily mean lack of support; it means there was a limit to how much I could type, or I couldn't quickly find the information in a search. If your software package isn't listed, searching for its name plus sni
should reveal if support exists and how to set it up.)
Library Support
Most packages depend on an external library to provide SSL/TLS support.
- GNU TLS
- JSSE (Oracle Java) 7 or higher, only as a client
- libcurl 7.18.1 or higher
- NSS 3.1.1 or higher
- OpenSSL 0.9.8j or higher
- OpenSSL 0.9.8f or higher, with configure flags
- Qt 4.8 or higher
Server Support
Most current versions of popular server software support SNI. Setup instructions are available for most of these:
- Apache 2.2.12 or higher
- Apache Traffic Server 3.2.0 or higher
- Cherokee
- HAProxy 1.5 or higher
- IIS 8.0 or higher
- lighttpd 1.4.24 or higher
- LiteSpeed 4.1 or higher
- nginx 0.5.32 or higher
Client Support
Most current web browsers and command line user agents support SNI.
Desktop
- Chrome 5 or higher
- Chrome 6 or higher on Windows XP
- Firefox 2 or higher
- Internet Explorer 7 or higher, running on Windows Vista/Server 2008 or higher
- Internet Explorer on Windows XP does not support SNI regardless of IE version
- Konqueror 4.7 or higher
- Opera 8 or higher (may require TLS 1.1 enabled to function)
- Safari 3.0 on Windows Vista/Server 2008 or higher, or Mac OS X 10.5.6 or higher
Mobile
- Android Browser on 3.0 Honeycomb or higher
- iOS Safari on iOS 4 or higher
- Windows Phone 7 or higher
Command Line
- cURL 7.18.1 or higher
- wget 1.14 or higher (Distributions may have backported a patch for SNI support)
No Support
- BlackBerry Browser
- Internet Explorer (any version) on Windows XP
(Note: Some information for this answer was obtained from Wikipedia.)
Solution 3:
The problem:
When a web client and a web server talk to each other over HTTPS, the very first thing that needs to happen is the secure handshake.
Here is a simplified example of such a handshake:
If this were HTTP and not HTTPS, the first thing the client would have sent would have been something like this:
GET /index.html HTTP/1.1
Host: example.com
This made multiple virtual hosts on a single IP address possible, since the server knows exactly what domain the client wants to access, namely example.com.
HTTPS is different. Like I said earlier, the handshake comes before everything else. If you look at the third step of the handshake illustrated above (Certificate), the server needs to present a certificate to the client as part of the handshake, but has no clue what domain name the client is trying to access. The only option the server has is to send the same certificate every time, its default certificate.
You could still set up virtual hosts on your web server, but the server would always send the same certificate to each client. If you tried to host both the example.com and example.org websites on your server, the server would always send the certificate for example.com when a client requests a HTTPS connection. So when a client requests example.org over an established HTTPS connection, this would happen:
This problem effectively limits the number of domains you can server over HTTPS to one per IP address.
The solution:
The easiest way to solve this problem is for the client to tell the server which domain it wants to access during the handshake. This way the server can serve up the correct certificate.
This is exactly what SNI, or Server Name Indication does.
With SNI, the client sends the server name it wants to access as part of the first message, the "Client Hello" step in the handshake diagram above.
Some older web browsers do not support SNI. For instance, on Windows XP there isn't a single version of Internet Explorer that has support for SNI. When accessing a resource over HTTPS on a server that makes use of SNI virtual hosts, you will be presented with a generic certificate, which may cause the browser to display a warning or error.
I've simplified things here to just explain the principle behind the problem and the solution. If you would like a more technical explanation, the wikipedia page or RFC 6066 might serve as good starting points. You can also find an up to date of list of servers and browsers that support SNI on wikipedia