Configurar correctamente un servidor nginx "predeterminado" para https

71

Tengo varios servidores ejecutándose en la misma máquina, algunos solo con http, algunos con http y https. Hay varios bloques de servidor definidos en archivos separados que se incluyen desde el archivo de configuración principal.

He configurado un servidor "predeterminado" para http que servirá una "página de mantenimiento" genérica para solicitudes que no coincidan con ninguno de los otros nombres de servidor en los otros archivos de configuración. El servidor predeterminado http funciona como se espera, utiliza el nombre_servidor "_" y aparece primero en la lista de inclusiones (porque he observado que en el caso de nombres_servidores duplicados en los servidores, se usa el que aparece primero). Esto funciona muy bien.

Esperaría el mismo bloque de servidor exacto (solo cambiando "listen 80 default_server" a "listen 443 default_server" y también en lugar de servir la página "return 444"), pero no lo hace. En cambio, parece que el nuevo servidor https predeterminado en realidad está tomando todas las conexiones https entrantes y provoca que fallen, aunque los otros bloques del servidor tienen nombres de servidor más apropiados para las solicitudes entrantes. La eliminación del nuevo servidor https predeterminado hará que se reanude el comportamiento semi-correcto: todos los sitios web con https se cargarán correctamente; pero los sitios web sin https se enrutarán al primer servidor https en los archivos de inclusión (que según los documentos, si no aparece "default_server", el primer bloque del servidor que aparecerá será "predeterminado").

Entonces mi pregunta es, ¿cuál es la forma correcta de definir un "servidor predeterminado" en nginx para conexiones SSL? ¿Por qué es que cuando establezco explícitamente un "servidor predeterminado" se vuelve codicioso y toma todas las conexiones, mientras que cuando implícitamente dejo que nginx decida el "servidor predeterminado" funciona como esperaría (con el servidor incorrecto configurado como predeterminado y los otros servidores reales comportarse correctamente)?

Aquí están mis "servidores predeterminados". Http funciona sin romper otros servidores. Https rompe otros servidores y consume todo.

server {
    listen 443 ssl default_server;
    server_name _;

    access_log /var/log/nginx/maintenance.access.log;
    error_log /var/log/nginx/maintenance.error.log error;

    return 444;
}

server {
    listen *:80 default_server;
    server_name _;
    charset utf-8;

    access_log /var/log/nginx/maintenance.access.log;
    error_log /var/log/nginx/maintenance.error.log error;

    root /home/path/to/templates;

    location / {
        return 503;
    }

    error_page 503 @maintenance;

    location @maintenance {
        rewrite ^(.*)$ /maintenance.html break;
    }
}

¿Alguno de ustedes ve lo que podría estar mal aquí?


fuente

Respuestas:

27

No tiene ningún ssl_certificate o ssl_certificate_key definido en su bloque https "predeterminado". Aunque no tiene o no desea una clave real para este escenario predeterminado, aún necesita configurar una o de lo contrario nginx tendrá el comportamiento no deseado que usted describe.

Cree un certificado autofirmado con un nombre común de * y conéctelo a su configuración y comenzará a funcionar como desee.

El comportamiento "predeterminado" en esta configuración sería que un navegador recibiría una advertencia de que no se puede confiar en el certificado, si el usuario agrega el certificado como una excepción, nginx desconectará la conexión y verá el valor predeterminado de su navegador mensaje de error "no se pudo conectar".

Radmilla Mustafa
fuente
1
He intentado esto pero todavía no funciona: todas las solicitudes de SSL a la IP van a mi otro host SSL. ¿Algo más que pueda probar?
Michael Härtl
23

Logré configurar un alojamiento dedicado compartido en una sola IP con nginx. HTTP y HTTPS predeterminados que sirven un 404 para dominios desconocidos entrantes.

1 - Crear una zona predeterminada

Como nginx está cargando vhosts en orden ascii, debe crear un 00-defaultarchivo / enlace simbólico en su /etc/nginx/sites-enabled.

2 - Rellena la zona predeterminada

Llena tus 00-defaultvhosts predeterminados. Aquí está la zona que estoy usando:

server {
    server_name _;
    listen       80  default_server;
    return       404;
}


server {
    listen 443 ssl;
    server_name _;
    ssl_certificate /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;
    return       404;
}

3 - Crear certif autofirmado, probar y recargar

Deberá crear un certificado autofirmado en /etc/nginx/ssl/nginx.crt.

Cree un certificado autofirmado predeterminado:

sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt

Simplemente un recordatorio:

  • Pruebe la configuración de nginx antes de volver a cargar / reiniciar: nginx -t
  • Recargar un disfrutar: sudo service nginx reload

Espero eso ayude.

Si no
fuente
2
No resuelve la advertencia del navegador de visitar un sitio inseguro.
gdbj
55
No se puede resolver ya que un conjunto debe coincidir con cualquier dominio. Ningún SSL puede poner comodines en todos los dominios. Imagine que está falsificando la dirección de google.fr en su servidor, podrá autenticar su servidor como google.fr. Sería un grave problema de seguridad :(
Si no,
Sí, supongo que tiene sentido. Desafortunadamente, en Chrome, aparece una advertencia aterradora antes de ver la página 404, que es peor que hacer que el servidor simplemente rechace el tráfico. Hace que parezca que el servidor está mal configurado.
gdbj
Muchas gracias. Esto funcionó para mí, excepto que tuve que agregar default_serverpara escuchar 443 y agregué la dirección IPv6 [::]: 80 y [::]: 443 con default_server.
Chmike
16

Básicamente, queremos evitar a toda costa que la primera definición de servidor en nuestro archivo de configuración se sirva como un servidor general para las conexiones SSL. Todos sabemos que lo hace (a diferencia de http y que usa la configuración default_server que funciona bien).

Esto no se puede lograr declarativamente para SSL (todavía), por lo que tenemos que codificarlo con un IF ...

La variable $hostes el nombre de host de la línea de solicitud o el encabezado http. La variable $server_namees el nombre del bloque de servidor en el que estamos ahora.

Entonces, si estos dos no son iguales, entonces sirvió este bloque de servidor SSL para otro host, por lo que debería bloquearse.

El código no contiene referencias específicas a las direcciones IP de su servidor, por lo que puede reutilizarse fácilmente para otras configuraciones de servidor sin modificación.

Ejemplo:

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

    ###
    ### Section: SSL

    #
    ## Check if this certificate is really served for this server_name
    ##   http://serverfault.com/questions/578648/properly-setting-up-a-default-nginx-server-for-https
    if ($host != $server_name) {
        #return 404 "this is an invalid request";
        return       444;
    }

    ...
Rolf
fuente
¿podrías explicar qué listen [::]:443 ssl http2;hace? Tengo problemas para encontrar documentación para ello.
Andrew Brown el
Creo que lo encontré, solo necesitaba saber qué buscar. Directivas IPV4 e IPV6.
Andrew Brown
7

Para elaborar más sobre la respuesta de Radmilla Mustafa:

Nginx usa el encabezado 'Host' para la coincidencia del nombre del servidor. No utiliza TLS SNI. Esto significa que para un servidor SSL, nginx debe poder aceptar la conexión SSL, lo que se reduce a tener certificado / clave. El certificado / clave puede ser cualquiera, por ejemplo, autofirmado.

Ver documentación

Por lo tanto, la solución es:

server {
    server_name _;
    listen 80 default_server;
    listen 443 ssl default_server;

    ## To also support IPv6, uncomment this block
    # listen [::]:80 default_server;
    # listen [::]:443 ssl default_server;

    ssl_certificate <path to cert>;
    ssl_certificate_key <path to key>;
    return 404; # or whatever
}
andreycpp
fuente
Siento que esta debería ser la respuesta aceptada. Muchas gracias.
agregado11686877
2

A cualquiera que haya perdido tanto pelo por esto como yo (pasé casi todo el día en esto hoy). He intentado casi todo, y lo que finalmente hizo que funcionara correctamente fue esta estúpida línea:

ssl_session_tickets off;

Sobre la base de la respuesta de Ifnot , mi ejemplo de trabajo es:

server {
    listen 443 ssl http2 default_server;
    listen [::]:443 ssl http2 default_server;
    server_name _;

    ssl_certificate /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;
    ssl_session_tickets off;

    return 404;
}

No tengo idea de por qué esto era necesario, el único principio que saqué de esto fue que nginx se comporta de manera muy extraña cuando no le damos lo que quiere.

Marek Lisý
fuente
1
eres una leyenda
Nizar Blond
1

Si desea estar absolutamente seguro, utilice direcciones IP separadas para los hosts que no deberían responder en HTTPS y los hosts que deberían responder. Esto también resuelve el problema de advertencia del navegador "certificado no válido".

Michael Hampton
fuente