¿Por qué nginx responde a cualquier nombre de dominio?

139

Tengo nginx funcionando con una aplicación Ruby / Sinatra y todo está bien. Sin embargo, ahora estoy tratando de tener una segunda aplicación ejecutándose desde el mismo servidor y noté algo extraño. Primero, aquí está mi 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

Observe cómo server_nameestá configurado para que FAKE.COMel servidor responda a todos los hosts que llegan a ese servidor a través de otros nombres de dominio. ¿Cómo puedo hacer que ese servidor en particular responda solo a las solicitudes FAKE.COM?

Martín
fuente
el listen fake.com | something.com:80 comando filtra, no server_name.
Alexei Martchenko

Respuestas:

202

El primer bloque de servidor en la configuración de nginx es el predeterminado para todas las solicitudes que llegan al servidor para el que no hay un bloque de servidor específico.

Entonces, en su configuración, suponiendo que su dominio real es REAL.COM, cuando un usuario escribe eso, se resolverá en su servidor, y dado que no hay un bloqueo de servidor para esta configuración, el bloque de servidor para FAKE.COM es el primero bloque de servidor (solo bloque de servidor en su caso) procesará esa solicitud.

Esta es la razón por la cual las configuraciones adecuadas de Nginx tienen un bloque de servidor específico para los valores predeterminados antes de seguir con otros para dominios específicos.

# Default server
server {
    return 404;
}

server {
    server_name domain_1;
    [...]
}

server {
    server_name domain_2;
    [...]
}

etc.

** EDITAR **

Parece que algunos usuarios están un poco confundidos con este ejemplo y piensan que está limitado a un solo archivo conf, etc.

Tenga en cuenta que lo anterior es un ejemplo simple para que el OP se desarrolle según sea necesario.

Personalmente utilizo archivos conf de vhost separados con esto como tal (CentOS / RHEL):

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

/etc/nginx/conf.d/ contendrá domain_1.conf, domain_2.conf ... domain_n.conf que se incluirá después del bloque del servidor en el archivo nginx.conf principal, que siempre será el primero y siempre será el predeterminado a menos que se anule con el servidor predeterminado directiva en otro lugar.

El orden alfabético de los nombres de los archivos conf para los otros servidores se vuelve irrelevante en este caso.

Además, esta disposición ofrece mucha flexibilidad, ya que es posible definir múltiples valores predeterminados.

En mi caso específico, tengo a Apache escuchando en el puerto 8080 solo en la interfaz interna y proxy los scripts PHP y Perl a Apache.

Sin embargo, ejecuto dos aplicaciones separadas que devuelven enlaces con ": 8080" en el html de salida adjunto, ya que detectan que Apache no se está ejecutando en el puerto 80 estándar e intento "ayudarme".

Esto causa un problema porque los enlaces se vuelven inválidos ya que no se puede acceder a Apache desde la interfaz externa y los enlaces deben apuntar al Puerto 80.

Resuelvo esto creando un servidor predeterminado para el Puerto 8080 para redirigir tales solicitudes.

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

Como nada en los bloques de servidor normales escucha en el Puerto 8080, el bloque de servidor predeterminado de redireccionamiento maneja de manera transparente tales solicitudes en virtud de su posición en nginx.conf.

De hecho, tengo cuatro de esos bloques de servidores y este es un caso de uso simplificado.

Dayo
fuente
1
Nginx requiere mayúsculas y minúsculas: "Servidor" debería ser servidor y "Devolver" debería ser devuelto. Espero que esto ahorre algunos problemas al copiar este código.
Capaj
2
estático, consulte la respuesta de Oleg Neumyvkin: si tiene varios archivos de configuración en sitios disponibles, entonces el primer servidor en el primer archivo por orden alfabético es el predeterminado. Sospecho que esto podría ser un problema. Además, muchas distribuciones ejecutan un 'nginx -t' para probar la configuración antes de reiniciar; es posible que tenga un error que impida un reinicio.
jwhitlock
2
Esto está incompleto y no debe ser la respuesta aceptada. Para que esto funcione, también deberá eliminar default_server de las directivas de escucha.
Ben
@ben En primer lugar, el OP no tenía "default_server" en su ejemplo de problema y la respuesta se adapta a los detalles de esa pregunta. En segundo lugar, por qué alguien definiría default_server en una ubicación de servidor separada cuando se siguen las instrucciones para hacer que el primer servidor definido sea el predeterminado. En cualquier caso, si se ha definido específicamente un servidor por defecto, entonces este conjunto de preguntas y respuestas no es lo que debe considerarse para resolver cualquier problema que pueda tener.
Dayo
1
@Dayo Tiene razón en que se dirigió a la configuración específica publicada por el OP. Pero para una respuesta completa de la pregunta formulada, creo que es necesario mencionar el servidor predeterminado. Es muy posible leer su respuesta y no darse cuenta de cómo default_server interferiría con ella. Es aún más probable porque algunas distribuciones se envían con default_server definido en un archivo que puede no ser obvio para el usuario.
Ben
62

Debe tener un servidor predeterminado para capturar todo , puede devolver 404o mejor no responder en absoluto (ahorrará algo de ancho de banda) devolviendo, 444que es una respuesta HTTP específica de nginx que simplemente cierra la conexión y no devuelve nada

server {
    listen       80  default_server;
    server_name  _; # some invalid name that won't match anything
    return       444;
}
iTech
fuente
Me funcionó para nginx 1.8.0. El server_name _;que no tenía en versiones anteriores para nginx, pero funcionó. Ahora, para las versiones más nuevas de nginx, parece que necesita el server_name _;. Gracias
daniel
Resuelto Olvidé un punto y coma; _;
user1201917
2
@ iTech: Por alguna razón, devuelve 444 para todas las solicitudes. ¿Alguna pista?
Divick
Gran consejo sobre el 444, y en mi opinión, una solución mucho más limpia que devolver un código de error.
qqilihq
1
Es importante especificar un certificado / clave, de lo contrario todas las conexiones SSL coincidirán y fallarán, como señaló @AndreyT en su respuesta a continuación.
Mark Fletcher
35

No pude resolver mi problema con ninguna de las otras respuestas. Resolví el problema comprobando si el host coincidía y devolví un 403 si no era así. (Tenía un sitio web aleatorio que apuntaba al contenido de mis servidores web. Supongo que secuestraría el rango de búsqueda)

server {
    listen 443;
    server_name example.com;

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

    ...
}
Matt Carrier
fuente
1
Si es una mala práctica: nginx.com/resources/wiki/start/topics/depth/ifisevil
Esolitos
1
Hay casos en los que simplemente no puede evitar usar un if, por ejemplo, si necesita probar una variable que no tiene una directiva equivalente.
Edward
44
@Esolitos Literalmente, la tercera línea de ese artículo dice que este caso de uso está bien. Entiendo la necesidad de ser cauteloso, pero no agitemos los dedos en casos de uso razonable.
Poder
En mi opinión, la solución más fácil y concisa, ya que solo requiere 3 líneas de código que con gusto puede pegar en cualquier archivo vhost, luego solo cambia el $ host comparado.
Akito
28

Para responder a su pregunta, nginx elige el primer servidor si no hay coincidencia. Ver documentación :

Si su valor no coincide con ningún nombre de servidor, o si la solicitud no contiene este campo de encabezado, nginx enrutará la solicitud al servidor predeterminado para este puerto. En la configuración anterior, el servidor predeterminado es el primero ...

Ahora, si desea tener un servidor predeterminado que, por ejemplo, responda con 404 a todas las solicitudes, a continuación, le mostramos cómo hacerlo:

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

Tenga en cuenta que debe especificar el certificado / clave (que puede ser autofirmado), de lo contrario, todas las conexiones SSL fallarán, ya que nginx intentará aceptar la conexión usando este default_server y no encontrará cert / key.

andreycpp
fuente
3
No tengo idea de por qué esta respuesta está tan abajo en la lista. Este es el que responde a la pregunta sin distraerse con cosas brillantes en el camino.
mmc
Esto me ayudó a resolver un problema en el que intentaba una redirección de no www a www en la que tenía que incluir mi certificado SSL en esta ruta de redireccionamiento; de lo contrario, intentaba obtener mi certificado SSL predeterminado que era para un dominio diferente.
endyourif
1
Esta parece ser la mejor respuesta para la mayoría de las configuraciones ... no estoy seguro de quién está usando nginx sin SSL en estos días, pero el hecho de que este sea el único que cubre SSL es extremadamente revelador.
Poder
Parece que server_name _;ni siquiera es necesario.
Julien Salinas
26

Hay pocas formas de especificar el servidor predeterminado.

Primera forma : especifique el servidor predeterminado primero en la lista, si mantiene las configuraciones de su servidor en un archivo de configuración, como Dayo mostró anteriormente.

Segunda forma (mejor) Más flexible: proporcione default_serverparámetros para la listeninstrucción, por ejemplo:

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

Más información aquí: Nginx doc / Listen

De esta forma es más útil cuando mantiene las configuraciones del servidor en archivos separados y no desea nombrar alfabéticamente esos archivos.

Pavel
fuente
3
Esta debería ser la respuesta correcta. La respuesta aceptada es engañosa. Simplemente agregar otro servidor a la configuración no resolverá el problema si otro servidor está configurado para ser el servidor predeterminado.
Ben
1
@Pavel no hay ninguna razón por la cual la respuesta proporcionada no pueda funcionar con múltiples archivos vhost separados. Además, con esto, sé que mi servidor predeterminado siempre está en el archivo nginx.conf principal y no tengo que recordar cuál de mis muchos archivos vhost separados contiene esto.
Dayo
No olvide escuchar 443 también para ssl (vea la respuesta de @ AndreyT a continuación).
Constantinos
8

Pequeño comentario para responder:

si tiene varios hosts virtuales en varias IP en varios archivos de configuración en sitios disponibles /, el dominio "predeterminado" para IP se tomará del primer archivo por orden alfabético.

Y, como dijo Pavel, hay un argumento de "servidor_de_determinado" para la directiva "escuchar" http://nginx.org/en/docs/http/ngx_http_core_module.html#listen

Oleg Neumyvakin
fuente