Como configuro vários subdomínios com seu próprio certificado usando o nginx?

8

A menos que todas as respostas que eu li estejam completamente erradas, o SNI deve tornar possível fazer o que quero, mas todos os guias me dizem para fazer exatamente o que estou fazendo.

E, no entanto, o nginx está servindo o certificado errado, por isso estou claramente fazendo algo errado.

❯ 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

Aqui está a aparência das minhas configurações:

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;
}

Se eu tiver a listen 443 ssl default_server;diretiva em qualquer servidor, ela retornará o certificado SSL para esse servidor para ambos os domínios. Se eu removê-lo dos dois domínios, não recebo nada - os dois domínios do servidor recusam conexões.

O que eu tenho de errado aqui? Eu simplesmente não entendo como o SNI funciona? Meu nginx foi criado com o suporte SNI ativado. E, no entanto ... só recebo o certificado SSL para um subdomínio.

Wayne Werner
fonte
Como você testa? Se você testar, openssl s_clientadicione a -servername hostnameopção para que o cliente realmente use o SNI.
Steffen Ullrich 11/11
O @SteffenUllrich Chrome é uma maneira de fazer isso. Apenas tentei usar openssl s_client -servername two.example.com -connect two.example.com:443e isso me dá a CN para one.example.com. Se eu trocar qual deles tem o servidor padrão, entendi o contrário.
Wayne Werner
E para chutes e sorrisos, eu apenas misturei: -servername one -connect twoe vice-versa. O openssl s_client e o chrome observam o mesmo comportamento - a única característica definidora aparente é a linha do servidor padrão.
Wayne Werner
@SteffenUllrich, parece que a sintaxe do IPv6 para ouvir faz algo diferente? Eu postei uma resposta, mas se isso desencadeia algo para você que eu adoraria saber mais sobre por que
Wayne Werner
Quando tenho vários sites em um único servidor, prefiro ter um default_serverbloco completamente separado que não retorna nenhum site.
Tero Kilkanen

Respostas:

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

A primeira linha permite escutar na porta 443 no IPv4. A segunda linha cobre apenas o IPv6. Como você possui apenas uma única listen 443configuração (IPv4), é a que é usada se você se conectar ao IPv4. Se você tentar se conectar ao IPv6, o SNI deverá mostrar o comportamento esperado.

Em vez disso, você provavelmente pode usar o servidor padrão:

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

E para o outro servidor

  listen 443 ssl;
  listen [::]:443 ssl;
Steffen Ullrich
fonte
2

Aparentemente, tem algo a ver com a sintaxe de escuta do IPv6. Quando eu mudo

listen [::]:443 ssl;

para

listen 443 ssl;

Então funciona.

Não sei por que isso acontece e gostaria de receber outras respostas com mais / melhores explicações.

Wayne Werner
fonte
Então você se conectou ao IPv4 ou IPv6 no servidor? Se você se conectou ao IPv4, fica claro porque você tinha apenas uma configuração IPv4 - o servidor padrão. Para fazer a configuração IPv6 também cobrir IPv4 você precisa adicionaripv6only=off;
Steffen Ullrich