Configurare correttamente un server nginx "predefinito" per https


71

Ho diversi server in esecuzione sulla stessa macchina, alcuni solo con http, altri con http e https. Esistono diversi blocchi server definiti in file separati inclusi nel file di configurazione principale.

Ho impostato un server "predefinito" per http che servirà una "pagina di manutenzione" generica per le richieste che non corrispondono a nessuno degli altri nomi_server negli altri file di configurazione. Il server predefinito http funziona come previsto, utilizza il nome_server "_" e appare per primo nell'elenco di include (poiché ho osservato che nel caso di nomi di server duplicati tra server, viene utilizzato quello visualizzato per primo). Funziona benissimo.

Mi aspetterei lo stesso blocco server esatto (cambiando solo "ascolta 80 default_server" su "ascolta 443 default_server" e anche invece di servire la pagina "return 444"), tuttavia non lo è. Al contrario, sembra che il nuovo server https predefinito stia effettivamente afferrando tutte le connessioni https in entrata e causandone il fallimento, sebbene gli altri blocchi server abbiano nomi_server più appropriati per le richieste in arrivo. La rimozione del nuovo server https predefinito causerà il ripristino del comportamento semi-corretto: i siti Web con https verranno caricati correttamente; ma i siti Web senza https verranno tutti indirizzati al primo server https nei file include (che secondo i documenti, se non appare "default_server", il primo blocco server che appare sarà "predefinito").

Quindi la mia domanda è: qual è il modo corretto di definire un "server predefinito" in nginx per le connessioni ssl? Perché quando imposto esplicitamente un "default_server" diventa avido e afferra tutte le connessioni mentre quando lascio implicitamente a nginx decidere il "server predefinito" funziona come mi aspetterei (con il server errato impostato come predefinito e gli altri server reali comportarsi correttamente)?

Ecco i miei "server predefiniti". Http funziona senza interrompere altri server. Https rompe altri server e consuma tutto.

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

Qualcuno di voi vede cosa potrebbe esserci di sbagliato qui?

ssl  https  nginx 

Risposte:


27

Non hai alcun ssl_certificate o ssl_certificate_key definito nel tuo blocco https "predefinito". Sebbene non si disponga o si desideri una chiave reale per questo scenario predefinito, è comunque necessario configurare uno o l'altro nginx avrà il comportamento indesiderato descritto.

Crea un certificato autofirmato con un nome comune di * e collegalo alla tua configurazione e inizierà a funzionare come desideri.

Il comportamento "predefinito" in questa configurazione sarebbe che un browser riceverà un avviso che il certificato non può essere considerato attendibile, se l'utente aggiunge il certificato come eccezione, la connessione verrà interrotta da nginx e vedranno l'impostazione predefinita del browser messaggio di errore "impossibile connettersi".


1
Ci ho provato ma non funziona ancora: tutte le richieste SSL all'IP vanno al mio altro host SSL. Qualcos'altro che potrei provare?
Michael Härtl,

23

Sono riuscito a configurare un hosting dedicato condiviso su un singolo IP con nginx. HTTP e HTTPS predefiniti che servono un 404 per domini sconosciuti in arrivo.

1 - Crea una zona predefinita

Poiché nginx sta caricando i vhosts in ordine ASCII, è necessario creare un 00-defaultcollegamento file / simbolico nel proprio /etc/nginx/sites-enabled.

2 - Riempi la zona predefinita

Riempi il tuo 00-defaultcon vosts predefiniti. Ecco la zona che sto 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 - Creare un certificato autofirmato, testare e ricaricare

Dovrai creare un certificato autofirmato in /etc/nginx/ssl/nginx.crt.

Crea un certificato autofirmato predefinito:

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

Solo un promemoria:

  • Testare la configurazione di nginx prima di ricaricare / riavviare: nginx -t
  • Ricarica un divertimento: sudo service nginx reload

Spero che sia d'aiuto.


2
Non risolve l'avviso del browser di visitare un sito non sicuro.
gdbj,

5
Non può essere risolto poiché un catch-all deve corrispondere a qualsiasi dominio. Nessun SSL può jolly tutti i domini. Immagina se stai falsificando l'indirizzo google.fr sul tuo server, sarai in grado di autenticare il tuo server come google.fr. Sarebbe un grave problema di sicurezza :(
Ifnot

Ya, immagino abbia senso. Sfortunatamente in Chrome, ti viene presentato un avviso spaventoso prima di vedere la pagina 404 che è peggio che avere il server semplicemente rifiutare il traffico. Sembra che il server non sia configurato correttamente.
gdbj,

Grazie mille. Questo ha funzionato per me, tranne per il fatto che ho dovuto aggiungere default_serverper l'ascolto 443 e ho aggiunto l'indirizzo IPv6 [::]: 80 e [::]: 443 con default_server.
chmike,

16

Fondamentalmente vogliamo evitare a tutti i costi che la prima definizione del server nel nostro file di configurazione sia servita come un server catch-all per le connessioni SSL. Sappiamo tutti che lo fa (al contrario di http e usando la configurazione di default_server che funziona bene).

Questo non può essere ottenuto in modo dichiarativo per SSL (ancora), quindi dobbiamo codificarlo con un IF ...

La variabile $hostè il nome host dalla riga della richiesta o dall'intestazione http. La variabile $server_nameè il nome del blocco server in cui ci troviamo adesso.

Quindi, se questi due non sono uguali, hai servito questo blocco del server SSL per un altro host, quindi dovrebbe essere bloccato.

Il codice non contiene riferimenti specifici agli indirizzi IP del server, quindi può essere facilmente riutilizzato per altre configurazioni del server senza modifiche.

Esempio:

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

    ...

potresti spiegare cosa listen [::]:443 ssl http2;fa? Sto riscontrando problemi nel trovare la documentazione per questo.
Andrew Brown,

Penso di averlo trovato, avevo solo bisogno di sapere cosa cercare. Direttive IPV4 e IPV6.
Andrew Brown,

7

Per approfondire la risposta di Radmilla Mustafa:

Nginx utilizza l'intestazione "Host" per la corrispondenza server_name. Non utilizza TLS SNI. Ciò significa che per un server SSL, nginx deve essere in grado di accettare la connessione SSL, che si riduce a disporre di certificato / chiave. La chiave / certificato può essere qualsiasi, ad esempio autofirmato.

Vedi documentazione

Quindi, la soluzione è:

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
}

Sento che questa dovrebbe essere la risposta accettata. Grazie molto.
aggregate1166877,

2

A chiunque abbia perso così tanti capelli su di me (ho trascorso quasi tutto il giorno su questo oggi). Ho provato quasi tutto, e la cosa che alla fine ha funzionato correttamente è stata questa stupida linea:

ssl_session_tickets off;

Sulla base della risposta di Ifnot , il mio esempio di lavoro è:

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

Non ho idea del perché questo fosse necessario, l'unico principio che ne ho tratto era che nginx si comporta in modo molto strano quando non gli diamo quello che vuole.


1
sei una leggenda.
Nizar Blond,

1

Se vuoi essere assolutamente sicuro, usa indirizzi IP separati per gli host che non dovrebbero rispondere su HTTPS e gli host che dovrebbero. Ciò risolve anche il problema di avviso del browser "certificato non valido".

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.