Multiple SSL domains on the same IP address and same port?

Solution 1:

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 2:

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 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:

tls 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:

enter image description here

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.

enter image description here

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


Solution 4:

http://wiki.apache.org/httpd/NameBasedSSLVHostsWithSNI

The client browser must also support SNI. Here are some browsers that do:

* Mozilla Firefox 2.0 or later
* Opera 8.0 or later (with TLS 1.1 enabled)
* Internet Explorer 7.0 or later (on Vista, not XP)
* Google Chrome
* Safari 3.2.1 on Mac OS X 10.5.6 

Solution 5:

The server name indication (RFC6066) TLS extension is required for name-based vhosts to work over HTTPS.

The extension is widely implemented and I have yet to encounter any issues with current software, but there is a chance that some clients (those not supporting it) will be routed to your default site if you depend on SNI.