10

Unless every answer that I've read was plain wrong, SNI should make it possible to do what I want, yet every guide tells me to do exactly what I'm doing.

And yet nginx is serving the wrong certificate so I'm clearly doing something wrong.

❯ sudo nginx -V | grep SNI                                                                                                                                                                                                                                                            %1
nginx version: nginx/1.10.3
built with OpenSSL 1.1.0f  25 May 2017
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-qJwWoo/nginx-1.10.3=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-z,relro -Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/nginx/ngi
nx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fa
stcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_reques
t_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_sub_module --wit
h-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-mail=dynamic --with-mail_ssl_module --add-dynamic-module=/build/nginx-qJwWoo/nginx-1.10.3/debian/modules/nginx-auth-pam --add-dynamic-module=/build/nginx-qJwWoo/nginx-1.10.3/debian/modules/nginx-dav-
ext-module --add-dynamic-module=/build/nginx-qJwWoo/nginx-1.10.3/debian/modules/nginx-echo --add-dynamic-module=/build/nginx-qJwWoo/nginx-1.10.3/debian/modules/nginx-upstream-fair --add-dynamic-module=/build/nginx-qJwWoo/nginx-1.10.3/debian/modules/ngx_http_substitutions_filter_m
odule

Here's what my configs look like:

server {
  listen 443 ssl default_server;
  listen [::]:443 ssl;

  server_name one.example.com;

  ssl on;
  ssl_certificate       /etc/letsencrypt/live/one.example.com/fullchain.pem;
  ssl_certificate_key   /etc/letsencrypt/live/one.example.com/privkey.pem;

  index index.html;
  root /var/www/one.example.com/site;
}

server {
  #listen 443 ssl default_server;
  listen [::]:443 ssl;

  server_name two.example.com;

  ssl on;
  ssl_certificate       /etc/letsencrypt/live/two.example.com/fullchain.pem;
  ssl_certificate_key   /etc/letsencrypt/live/two.example.com/privkey.pem;

  index index.html;
  root /var/www/two.example.com/site;
}

If I have the listen 443 ssl default_server; directive in either server it will return the SSL cert for that server for BOTH domains. If I remove it from both domains then I just get nothing at all - both server domains refuse connections.

What do I have going wrong here? Do I just not understand how SNI works? My nginx has been built with SNI support enabled. And yet... I only get the ssl cert served for one subdomain.

Wayne Werner
  • 709
  • 4
  • 14
  • 26
  • How do you test? If you test with `openssl s_client` make sure to add the `-servername hostname` option so that the client actually uses SNI. – Steffen Ullrich Oct 11 '17 at 21:35
  • @SteffenUllrich Chrome is one way I've been doing it. Just tried using `openssl s_client -servername two.example.com -connect two.example.com:443` and it gives me the CN for `one.example.com`. If I swap which one has the default server then I get things the other way around. – Wayne Werner Oct 11 '17 at 21:37
  • And for kicks and grins I just mixed it up: `-servername one -connect two` and then vice versa. Both openssl s_client and chrome observe the same behavior - the only apparent defining characteristic is the default server line. – Wayne Werner Oct 11 '17 at 21:43
  • @SteffenUllrich it would appear that the IPv6 syntax for listening does something different? I've posted an answer, but if that triggers something for you I'd love to know more about *why* – Wayne Werner Oct 11 '17 at 21:49
  • When I have multiple sites on a single server, I prefer to have a completely separate `default_server` block which doesn't return either site. – Tero Kilkanen Oct 12 '17 at 18:59

2 Answers2

12
listen 443 ssl default_server;
listen [::]:443 ssl;

The first line enables listening on port 443 on IPv4. The second line covers IPv6 only. Since you have only a single listen 443 (IPv4) configuration it is the one which gets used if you connect with IPv4. If you would try to connect with IPv6 instead SNI should show the expected behavior.

Instead you might probably use for the default server:

  listen 443 ssl default_server;
  listen [::]:443 ssl default_server;

And for the other server

  listen 443 ssl;
  listen [::]:443 ssl;
Steffen Ullrich
  • 12,227
  • 24
  • 37
3

It apparently has something to do with the IPv6 listen syntax. When I change

listen [::]:443 ssl;

to

listen 443 ssl;

Then it works.

I don't know why this is and would welcome other answers with more/better explanation.

BrokenBinary
  • 103
  • 4
Wayne Werner
  • 709
  • 4
  • 14
  • 26
  • So did you connect with IPv4 or IPv6 to the server? If you connected with IPv4 then it is clear because you had only one IPv4 configuration - the default server. To make the IPv6 configuration also cover IPv4 you need to add `ipv6only=off;` – Steffen Ullrich Oct 11 '17 at 21:49