Nginx – Can nginx stream work with multiple SSL certificates

nginxsslwindows-server-2008-r2

I'm trying to set up nginx as a front-end to IIS 7 so that I can set up multiple SSL certificates with single IP, something IIS does not support (shocker).

The way I see it, I neither need or should be interfering with the HTTP layer at all, or any of the unencrypted data for that matter (because of Exchange's NTLM Authentication that is bound to the TCP session instead of individual requests). I should set up nginx to handle only the SSL layer.

Luckily, it supports doing that! But not with multiple certificates, apparently?

Basically this is what I'm trying:

stream {
    upstream http_backend {
        server 192.168.1.1:80;
    }

    upstream https_backend {
        server 192.168.1.1:443;
    }

    server {
        listen 80;
        proxy_pass http_backend;
    }

    server {
        listen 443 ssl;

        ssl_certificate      certs/default.pem;
        ssl_certificate_key  certs/default.key;

        ssl_certificate      certs/domain1.pem;
        ssl_certificate_key  certs/domain1.key;

        ssl_certificate      certs/domain2.pem;
        ssl_certificate_key  certs/domain2.key;

        proxy_pass https_backend;
        proxy_ssl on;
    }
}

According to the documentation, ssl_certificate and ssl_certificate_key can be called multiple times, but for the purpose of specifying certificates of different types, not for different domains. In this case, the last pair simply overrides the previous ones, and becomes the only certificate you negotiate with when trying to access the server, regardless of the host name used.

In stream mode I cannot set up multiple server entries in the same port just changing server_name like I can in http mode, in fact server does not support server_name at all when used in the stream context, so it's unclear how I'm supposed to solve this.

I've been Googling all day and can't figure out a solution. Perhaps nginx just doesn't support what I need? And in which case, is there an alternative?

Any advice appreciated!

Best Answer

So I had an epiphany and managed to crack this problem.

Turns out you don't need to set up SSL mode or any certificate in the stream server entry to get ssl_preread to work, and this possibilitates an interesting workaround.

I can set up multiple server entries on arbitrary ports listed for localhost only, each one with a different certificate, and have a main server on port 443 routing the incoming connections to the correct one.

Here's a basic configuration with 3 certificates:

worker_processes  1;

events {
}

stream {

    // IIS server
    upstream https_backend {
        server 10.0.0.1:443;
    }

    // set up SSL session with the default certificate
    upstream default {
        server 127.0.0.1:4430;
    }
    server {
        listen 127.0.0.1:4430 ssl;

        ssl_certificate certs/default.pem;
        ssl_certificate_key certs/default.key;

        proxy_ssl on;
        proxy_pass https_backend;
    }

    // set up SSL session with certificate for marvel.com, www.marvel.com
    upstream marvel {
        server 127.0.0.1:4431;
    }
    server {
        listen 127.0.0.1:4431 ssl;

        ssl_certificate certs/marvel.pem;
        ssl_certificate_key certs/marvel.key;

        proxy_ssl on;
        proxy_pass https_backend;
    }

    // set up SSL session with certificate for dccomics.com, www.dccomics.com
    upstream dccomics {
        server 127.0.0.1:4432;
    }
    server {
        listen 127.0.0.1:4432 ssl;

        ssl_certificate certs/dccomics.pem;
        ssl_certificate_key certs/dccomics.key;

        proxy_ssl on;
        proxy_pass https_backend;
    }

    // route connection to the tunnel with correct certificate
    map $ssl_preread_server_name $upstream {
        marvel.com marvel;
        www.marvel.com marvel;

        dccomics.com dccomics;
        www.dccomics.com dccomics;

        default default;
    }
    server {
        listen 443;
        ssl_preread on;
        proxy_pass $upstream;
    }
}

So as far as SSL and NTLM Authentication goes this is working wonders, the only thing I'm lacking with this set up is the possibility to register the real user IP on IIS logs, since I can't set X-Forwarded-For in HTTP headers.