Perché nginx risponde a qualsiasi nome di dominio?


139

Ho nginx installato e funzionante con un'app Ruby / Sinatra e tutto va bene. Tuttavia, ora sto cercando di avere una seconda applicazione in esecuzione dallo stesso server e ho notato qualcosa di strano. Innanzitutto, ecco il mio nginx.conf:

pid /tmp/nginx.pid;
error_log /tmp/nginx.error.log;

events {
  worker_connections 1024;
  accept_mutex off;
}

http {
  default_type application/octet-stream;
  access_log /tmp/nginx.access.log combined;

  sendfile on;
  tcp_nopush on;
  tcp_nodelay off;

  gzip on;
  gzip_http_version 1.0;
  gzip_proxied any;
  gzip_min_length 500;
  gzip_disable "MSIE [1-6]\.";
  gzip_types text/plain text/xml text/css
             text/comma-separated-values
             text/javascript application/x-javascript
             application/atom+xml;

  upstream app {
    server unix:/var/www/app/tmp/sockets/unicorn.sock fail_timeout=0;
  }

  server {
    listen 80;
    client_max_body_size 4G;
    server_name FAKE.COM;

    keepalive_timeout 5;

    root /var/www/app/public;

    location / {
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;

      if (!-f $request_filename) {
        proxy_pass http://app;
        break;
      }
    }

    error_page 500 502 503 504 /500.html;
    location = /500.html {
      root /var/www/app/public;
    }
  }
}
                                                          68,0-1        B

Si noti come server_nameè impostato su FAKE.COMma il server sta rispondendo a tutti gli host che hanno raggiunto quel server tramite altri nomi di dominio. Come posso fare in modo che quel particolare server risponda solo alle richieste di FAKE.COM?


il listen fake.com | something.com:80 comando filtra, no server_name.
Alexei Martchenko,

Risposte:


202

Il primo blocco server nella configurazione di nginx è il valore predefinito per tutte le richieste che colpiscono il server per il quale non esiste un blocco server specifico.

Quindi nella tua configurazione, supponendo che il tuo dominio reale sia REAL.COM, quando un utente lo digita, si risolverà sul tuo server e poiché non esiste un blocco server per questa configurazione, il blocco server per FAKE.COM, essendo il primo blocco server (solo blocco server nel tuo caso), elaborerà tale richiesta.

Questo è il motivo per cui le configurazioni Nginx appropriate hanno un blocco server specifico per i valori predefiniti prima di seguire con altri per domini specifici.

# Default server
server {
    return 404;
}

server {
    server_name domain_1;
    [...]
}

server {
    server_name domain_2;
    [...]
}

eccetera

** MODIFICARE **

Sembra che alcuni utenti siano un po 'confusi da questo esempio e pensano che sia limitato a un singolo file conf ecc.

Si noti che quanto sopra è un semplice esempio di sviluppo dell'OP come richiesto.

Personalmente uso file di configurazione vhost separati con questo in questo modo (CentOS / RHEL):

http {
    [...]
    # Default server
    server {
        return 404;
    }
    # Other servers
    include /etc/nginx/conf.d/*.conf;
}

/etc/nginx/conf.d/ conterrà domain_1.conf, domain_2.conf ... domain_n.conf che verrà incluso dopo il blocco del server nel file nginx.conf principale che sarà sempre il primo e sarà sempre il predefinito a meno che non venga sovrascritto con il default_server direttiva altrove.

In questo caso, l'ordine alfabetico dei nomi dei file conf per gli altri server diventa irrilevante.

Inoltre, questa disposizione offre molta flessibilità in quanto è possibile definire più impostazioni predefinite.

Nel mio caso specifico, ho Apache in ascolto sulla porta 8080 solo sull'interfaccia interna e faccio il proxy degli script PHP e Perl su Apache.

Tuttavia, eseguo due applicazioni separate che restituiscono entrambi i collegamenti con ": 8080" nell'html di output allegato poiché rilevano che Apache non è in esecuzione sulla porta 80 standard e provano ad "aiutarmi".

Ciò causa un problema in quanto i collegamenti diventano non validi poiché Apache non può essere raggiunto dall'interfaccia esterna e i collegamenti devono puntare alla porta 80.

Risolvo questo creando un server predefinito per la porta 8080 per reindirizzare tali richieste.

http {
    [...]
    # Default server block for undefined domains
    server {
        listen 80;
        return 404;
    }
    # Default server block to redirect Port 8080 for all domains
    server {
        listen my.external.ip.addr:8080;
        return 301 http://$host$request_uri;
    }
    # Other servers
    include /etc/nginx/conf.d/*.conf;
}

Poiché nulla nei blocchi di server regolari è in ascolto sulla porta 8080, il blocco di server predefinito di reindirizzamento gestisce in modo trasparente tali richieste in virtù della sua posizione in nginx.conf.

In realtà ho quattro di questi blocchi di server e questo è un caso d'uso semplificato.


1
Nginx richiede la distinzione tra maiuscole e minuscole: "Server" deve essere server e "Return" deve essere restituito. Spero che questo risparmi qualche problema durante la copia di questo codice.
Capaj,

2
statico, vedi la risposta di Oleg Neumyvkin: se hai più file di configurazione nei siti disponibili, il primo server nel primo file in ordine alfabetico è il tuo valore predefinito. Ho il sospetto che questo potrebbe essere un problema. Inoltre, molte distribuzioni eseguono un 'nginx -t' per testare la configurazione prima di riavviare - potresti avere un errore che impedisce un riavvio.
jwhitlock,

2
Questo è incompleto e non dovrebbe essere la risposta accettata. Affinché ciò funzioni, dovresti anche rimuovere default_server da qualsiasi direttiva di ascolto.
Ben

@ben In primo luogo, il PO non aveva "default_server" nel suo esempio di problema e la risposta è adattata alle specifiche di quella domanda. In secondo luogo, il motivo per cui qualcuno dovrebbe definire default_server su una posizione del server separata quando segue le istruzioni per rendere il primo server definito l'impostazione predefinita è oltre me. In ogni caso, se un default_server è stato definito in modo specifico, questo set di domande / risposte non è quello che dovrebbe essere preso in considerazione per risolvere qualsiasi problema possano verificarsi.
Dayo,

1
@Dayo Hai ragione nel dire che hai indirizzato la configurazione specifica pubblicata dall'OP. Ma per una risposta completa alla domanda posta, penso che sia necessario menzionare default_server. È molto possibile leggere la tua risposta e non rendersi conto di come default_server interferirebbe con essa. È ancora più probabile perché alcune distro spedite con default_server definito in un file che potrebbe non essere ovvio per l'utente.
Ben

62

Dovresti avere un server predefinito per catch-all , puoi tornare 404o meglio non rispondere affatto (salverà un po 'di larghezza di banda) restituendo 444quale è la risposta HTTP specifica di nginx che semplicemente chiude la connessione e non restituisce nulla

server {
    listen       80  default_server;
    server_name  _; # some invalid name that won't match anything
    return       444;
}

Ha funzionato per me per nginx 1.8.0. Il server_name _;non avevo nelle versioni precedenti per nginx, ma ha funzionato. Ora per le nuove versioni di nginx sembra che tu abbia bisogno di server_name _;. Grazie
Daniele

Risolto. Ho dimenticato un punto e virgola; _;
user1201917

2
@iTech: per qualche motivo restituisce 444 per tutte le richieste. Qualche indizio?
Divick,

Ottimo consiglio su 444, e imho una soluzione molto più pulita rispetto alla restituzione di un codice di errore.
qqilihq,

1
È importante specificare un certificato / chiave, altrimenti tutte le connessioni SSL corrisponderanno e falliranno, come sottolineato da @AndreyT nella sua risposta di seguito.
Mark Fletcher,

35

Non sono stato in grado di risolvere il mio problema con nessuna delle altre risposte. Ho risolto il problema verificando se l'host corrispondeva e restituendo un 403 in caso contrario. (Avevo un sito Web casuale che puntava al contenuto dei miei server Web. Immagino di dirottare il grado di ricerca)

server {
    listen 443;
    server_name example.com;

    if ($host != "example.com") {
        return 403;
    }

    ...
}


1
Ci sono casi in cui semplicemente non puoi evitare di usare un if, ad esempio, se devi testare una variabile che non ha una direttiva equivalente.
Edward,

4
@Esolitos Letteralmente la terza riga dell'articolo afferma che questo caso d'uso va bene. Capisco la necessità di essere prudenti, ma non agitiamo le dita in casi d'uso ragionevoli.
Potenza

A mio avviso, la soluzione più semplice e concisa, in quanto richiede solo 3 righe di codice che puoi incollare volentieri in qualsiasi file vhost, cambiando solo il $ host confrontato, in seguito.
Akito,

28

Per rispondere alla tua domanda - nginx sceglie il primo server se non c'è corrispondenza. Vedi documentazione :

Se il suo valore non corrisponde a nessun nome di server o la richiesta non contiene affatto questo campo di intestazione, nginx indirizzerà la richiesta al server predefinito per questa porta. Nella configurazione sopra, il server predefinito è il primo ...

Ora, se si desidera avere un server catch-all predefinito che, ad esempio, risponda con 404 a tutte le richieste, ecco come farlo:

server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;
    ssl_certificate <path to cert>
    ssl_certificate_key <path to key>
    return 404;
}

Nota che devi specificare certificato / chiave (che può essere autofirmato), altrimenti tutte le connessioni SSL falliranno poiché nginx tenterà di accettare la connessione usando questo default_server e non troverà cert / chiave.


3
Non ho idea del perché questa risposta sia così in fondo alla lista. Questo è quello che risponde alla domanda senza essere distratto da cose luccicanti lungo la strada.
MMC

Questo mi ha aiutato a risolvere un problema in cui stavo provando un reindirizzamento da non www a www che dovevo includere il mio certificato SSL in questo percorso di reindirizzamento, altrimenti stava cercando di afferrare il mio certificato SSL predefinito che era per un dominio diverso.
Endyourif

1
Questa sembra la migliore risposta per la maggior parte delle configurazioni ... non sono sicuro di chi stia utilizzando nginx senza SSL in questi giorni, ma il fatto che questa sia l'unica che copre SSL è estremamente significativo.
Potenza

Sembra che server_name _;non sia nemmeno necessario.
Julien Salinas,

26

Esistono alcuni modi per specificare il server predefinito.

Primo modo : specificare il server predefinito prima nell'elenco, se si mantengono le configurazioni del server in un file di configurazione, come mostrato in precedenza da Dayo.

Secondo modo (migliore) Più flessibile: fornire un default_serverparametro per l' listenistruzione, ad esempio:

server {
    listen  *:80 default_server;
    root /www/project/public/;
}

Maggiori informazioni qui: Nginx doc / Listen

In questo modo è più utile quando si mantengono le configurazioni del server in file separati e non si desidera nominare tali file in ordine alfabetico.


3
Questa dovrebbe essere la risposta corretta. La risposta accettata è fuorviante. La semplice aggiunta di un altro server alla configurazione non risolverà il problema se un altro server è configurato come server predefinito.
Ben

1
@Pavel non c'è motivo per cui la risposta fornita non possa funzionare con più file vhost separati. Inoltre, con questo, so che il mio server predefinito è sempre nel file principale nginx.conf e non devo ricordare quale dei miei molti file vhost separati lo contiene.
Dayo,

Non dimenticare di ascoltare anche 443 per ssl (vedi la risposta di @ AndreyT di seguito).
Constantinos,

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.