¿Cómo configuro múltiples subdominios con su propio certificado usando nginx?

8

A menos que todas las respuestas que he leído sean completamente erróneas, SNI debería hacer posible lo que quiero, sin embargo, cada guía me dice que haga exactamente lo que estoy haciendo.

Y sin embargo, nginx está entregando el certificado incorrecto, así que claramente estoy haciendo algo mal.

❯ 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

Así es como se ven mis configuraciones:

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

Si tengo la listen 443 ssl default_server;directiva en cualquiera de los servidores, devolverá el certificado SSL para ese servidor para AMBOS dominios. Si lo elimino de ambos dominios, simplemente no obtengo nada, ambos dominios de servidor rechazan las conexiones.

¿Qué tengo de malo aquí? ¿No entiendo cómo funciona SNI? Mi nginx ha sido construido con soporte SNI habilitado. Y sin embargo ... solo obtengo el certificado SSL para un subdominio.

Wayne Werner
fuente
¿Cómo se prueba? Si realiza la prueba, openssl s_clientasegúrese de agregar la -servername hostnameopción para que el cliente realmente use SNI.
Steffen Ullrich
@SteffenUllrich Chrome es una forma en que lo he estado haciendo. Acabo de intentar usar openssl s_client -servername two.example.com -connect two.example.com:443y me da el CN ​​para one.example.com. Si cambio cuál tiene el servidor predeterminado, entonces obtengo las cosas al revés.
Wayne Werner
Y para las patadas y sonrisas, simplemente lo mezclé: -servername one -connect twoy luego viceversa. Tanto openssl s_client como chrome observan el mismo comportamiento: la única característica de definición aparente es la línea de servidor predeterminada.
Wayne Werner
@SteffenUllrich, ¿parecería que la sintaxis de IPv6 para escuchar hace algo diferente? He publicado una respuesta, pero si eso provoca algo para ti, me encantaría saber más sobre por qué
Wayne Werner
Cuando tengo varios sitios en un solo servidor, prefiero tener un default_serverbloque completamente separado que no devuelva ninguno de los sitios.
Tero Kilkanen

Respuestas:

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

La primera línea permite escuchar en el puerto 443 en IPv4. La segunda línea cubre solo IPv6. Como solo tiene una listen 443configuración única (IPv4), es la que se utiliza si se conecta con IPv4. Si intenta conectarse con IPv6, SNI debería mostrar el comportamiento esperado.

En su lugar, es probable que lo use para el servidor predeterminado:

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

Y para el otro servidor

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

Aparentemente tiene algo que ver con la sintaxis de escucha IPv6. Cuando cambio

listen [::]:443 ssl;

a

listen 443 ssl;

Entonces funciona.

No sé por qué esto es y agradecería otras respuestas con más / mejor explicación.

Wayne Werner
fuente
Entonces, ¿se conectó con IPv4 o IPv6 al servidor? Si se conectó con IPv4, está claro porque solo tenía una configuración de IPv4: el servidor predeterminado. Para hacer que la configuración de IPv6 también cubra IPv4, debe agregaripv6only=off;
Steffen Ullrich