nginx: come impedire che un blocco server SSL denominato esattamente agisca come catchall per tutto SSL


17

Ho un server web con molti server virtuali. Solo 1 dei quali è SSL. Il problema è che, poiché non esiste un blocco server catchall in ascolto per SSL, qualsiasi richiesta https verso gli altri siti viene servita dal blocco 1 SSL.

La mia configurazione, in sostanza, è simile a questa:

# the catch all
server {
  listen 80 default;

  # I could add this, but since I have no default cert, I cannot enable SSL,
  # and this listen ends up doing nothing (apparently).
  # listen 443; 

  server_name _;
  # ...
}

# some server
server {
  listen 80;
  server_name server1.com;
  # ...
}

# some other server ...
server {
  listen 80;
  server_name server2.com;
  # ...
}

# ... and it's https equivalent
server {
  listen 443;
  ssl on;
  server_name server2.com;
  # ...
}

Ora che non esiste un listener predefinito per 443, una richiesta simile https://server1.comfinirà per essere servita dal server2.comblocco https. Questo segue la logica di server_namenei documenti.

Se non vi è alcuna corrispondenza, verrà utilizzato un blocco {...} del server nel file di configurazione in base al seguente ordine:

  1. il blocco server con una direttiva di ascolto corrispondente contrassegnata come [default | default_server]
  2. il primo blocco server con una direttiva di ascolto corrispondente (o ascolto implicito 80;)

Qual è la soluzione preferita per questo problema? Devo impostare un certificato fittizio per il blocco catch all server solo per poter ascoltare 443 e gestire le richieste errate? Esiste un parametro di cui non sono a conoscenza che forza una corrispondenza esatta del nome host server?


Cosa vuoi che accada quando le persone provano ad accedere agli altri siti usando https?
David Schwartz,

Idealmente o vorrei che nginx non servisse affatto https a meno che il nome host non corrispondesse, o che reindirizzasse a http sullo stesso host.
numeri 1311407,

Risposte:


9

Idealmente, vorrei che nginx non servisse affatto https a meno che il nome host non corrispondesse, o che reindirizzasse a http sullo stesso host.

Nessuno dei due è possibile. La connessione da un client che va su https://foo.example.com/ non può essere accettata da nient'altro che un certificato SSL con "foo.example.com" come uno dei suoi nomi. Non è possibile reindirizzare fino a quando la connessione SSL non viene accettata.

Se si configura ciascun sito per SSL, un utente che fa clic sull'errore del certificato riceverà il sito richiesto. Se si configura un sito "catch all" per SSL che fornisce solo una pagina di errore e si configura l'hosting virtuale basato sul nome per l'unico sito che dovrebbe supportare SSL, è possibile fornire una pagina di errore ai client.

L'hosting virtuale SSL e HTTP non funziona bene insieme.


Questo è ciò che ho raccolto dopo aver letto i documenti. Speravo solo di aver perso qualcosa. Non mi importa affatto degli avvisi SSL. Non voglio che qualcuno entri in server1.com e si trovi a guardare la homepage di server2.com ... Non c'è davvero modo di dire a nginx di non accettare una richiesta?
numeri 1311407,

Se non accetta la richiesta, il primo sito non funzionerà. Deve accettare la richiesta per scoprire a quale sito l'utente sta tentando di accedere.
David Schwartz,

2
"La connessione da un client che va a foo.example.com non può essere accettata da nient'altro che un certificato SSL con" foo.example.com "come uno dei suoi nomi." - Ciò non è corretto, il server accetterà la richiesta e spetta al client verificare che il DN richiesto corrisponda al DN del certificato del server.
ColinM,

4

L'unico modo per fare è creare un certificato SSL autofirmato e usarlo per ottenere il controllo sulle richieste https in arrivo. Puoi creare il tuo certificato SSL autofirmato in pochi semplici passaggi descritti in questo post .

Supponiamo che tu crei un certificato autofirmato con un nome file di server.crt. Quindi aggiungerebbe quanto segue nella configurazione di nginx:

server {
    listen  443;

    ssl    on;
    ssl_certificate         /etc/nginx/ssl/server.crt;
    ssl_certificate_key     /etc/nginx/ssl/server.key;

    server_name server1.com;

    keepalive_timeout 60;
    rewrite ^       http://$server_name$request_uri? permanent;
}

Riceverai comunque il messaggio di avviso SSL del browser, ma almeno avrai il controllo su ciò che succederà dopo.


1
Sono d'accordo con questo. C'è il problema del browser che mostra un messaggio di avviso su certificati non attendibili, ma se stai solo cercando di impedire agli utenti di andare su https: // <indirizzo-ip> per ottenere server e un certificato altrettanto non valido per uno dei tuoi vhosts reali (non valido perché il nome host non corrisponde), è meglio offrire loro un certificato fittizio autofirmato non valido. Questo tipo di dice loro "non c'è niente da vedere qui, nemmeno un certificato da un altro host".
Daniel F,

2

Aggiungi un blocco server catch-all e restituisci il codice di stato 444. Indica a nginx di chiudere la connessione prima di inviare qualsiasi dato.

server {
    listen 443 default_server ssl;
    server_name _;
    return 444;
}

1

In questi giorni è possibile utilizzare l'estensione per l'indicazione del nome del server TLS (SNI, RFC 6066). Il listener HTTPS sarà in grado di riconoscere il nome di dominio prima di offrire il certificato appropriato.

Ciò significa che dovrai disporre di certificati per TUTTI i tuoi domini e quando SNI viene utilizzato per riconoscere uno degli altri domini puoi semplicemente utilizzare il reindirizzamento HTTP 301 alla versione non crittografata HTTP a meno che il nome del server non corrisponda a quello singolo che necessita crittografia.

Maggiori informazioni su SNI disponibili nella documentazione di nginx http://nginx.org/en/docs/http/configuring_https_servers.html


0

Mappare il nome host richiesto su nomi host validi nel http {}blocco:

map $ssl_server_name $correct_hostname_example {
  default 0;
  example.com 1;
  www.example.com 1;
}

E poi nel server {}blocco uccidi le connessioni con un nome host errato:

if ($correct_hostname_example = 0) {
  return 444;
}

Utilizzare più mappe se necessario per più blocchi server. La connessione verrà comunque stabilita utilizzando uno dei tuoi certificati ma se quest'ultimo blocco è presente in ogni blocco server che serve SSL, in questo modo "bloccherai" le connessioni con nomi host non validi. Potrebbe essere necessario solo nel primo blocco server, ma aggiungerlo a ogni blocco server garantirà che l'ordine non abbia importanza.

La $ssl_server_namevariabile è presente in nginx 1.7 o versioni successive.


0

Ecco come ho risolto il problema:

  1. Crea certificato autofirmato:

openssl req -nodes -x509 -newkey rsa:4096 -keyout self_key.pem -out self_cert.pem -days 3650

  1. Copia dove NginX può trovarlo:

cp self*.pem /etc/nginx/ssl/

  1. Imposta un percorso universale:
server {
    listen 443 default_server ssl;

    ssl on;
    ssl_certificate /etc/nginx/ssl/self_cert.pem;
    ssl_certificate_key /etc/nginx/ssl/self_key.pem;

    return 301 http://$host;
}

Cosa farà: ti darà un avviso (assolutamente impossibile) su qualsiasi server che non ha il proprio certificato, ma l'avviso non dirà il nome del certificato sbagliato. Se l'utente fa clic su "visita comunque", verrà reindirizzato alla versione non SSL del sito che ha digitato.

avvertimento :

se il tuo sito abilitato per SLL definisce www.example.com(e non example.com) solo il tuo percorso generale finirà per essere pubblicato https://example.comcon il certificato autofirmato e il relativo avviso.


-2

Reindirizzamento a http:

server {
    listen       443;
    server_name  *.com;
    rewrite        ^ http://$host$request_uri? permanent;
}    

Ritorno 404 :

server {
    listen       443;
    server_name  *.com;
    return 404;
}    

1
Ciò comporterà comunque un avviso SSL, poiché è necessario stabilire il tunnel SSL prima che si verifichi qualsiasi reindirizzamento HTTP. Vedi la risposta accettata di David Schwartz.
cjc
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.