Hacer que nginx pase el nombre de host del flujo ascendente cuando se invierte el proxy

89

Ejecuto varios contenedores docker con nombres de host:

web1.local web2.local web3.local

El enrutamiento a estos se realiza en función del nombre de host de nginx. Tengo un proxy en frente de esta configuración (en diferentes máquinas conectadas a internet) donde defino upstream como:

    upstream main {
      server web1.local:80;
      server web2.local:80;
      server web3.local:80;
    }

Y descripción real del host virtual:

    server {
      listen 80;
      server_name example.com;
      location / {
        proxy_pass http://main;
      }
    }

Ahora, debido a que los contenedores reciben el nombre de host "main" en lugar de "web1.local", no responden correctamente a la solicitud.

Pregunta: ¿cómo puedo decirle a nginx que pase el nombre del servidor en sentido ascendente en lugar del nombre del grupo de servidores en sentido ascendente en el encabezado cuando se envía la solicitud por proxy?

pavel_karoukin
fuente
3
No creo que puedas. ¿Por qué no configura sus servidores back-end para responder a main o example.com? No es como si el backend no sabe quién lo es. Lo contrario es fácilmente posible: proxy_set_header Host $ host; reemplazará cualquier variable de Host que regrese de la cadena ascendente con el nombre de host de la solicitud original.
Andrew Domaszek
Lo correcto es arreglar la aplicación.
Michael Hampton

Respuestas:

109

En realidad, puedes hacerlo a través de proxy_set_header.

Para obtener más detalles, consulte aquí: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header o vea un caso de uso de ejemplo aquí: https://stackoverflow.com/questions/12847771/configure-nginx- con-proxy-pass

He incluido el enfoque dinámico en su configuración publicada anteriormente:

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            $host;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}

Aquí hay un ejemplo con un nombre de host estático:

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            www.example.com;
    proxy_set_header X-Forwarded-For $remote_addr;
  }
}
Jens Bradler
fuente
77
proxy_set_header X-Fordered-For $ proxy_add_x_forwards_for; parece mejor
sivann
1
@pavel: lo tengo. En realidad también hice algunas investigaciones y algunas pruebas. Parece que no existe un enfoque directo para cumplir con su requisito. Entonces, incluso una solución "bastarda" es una solución. No me gusta preguntar por qué te gustaría hacer esto. Estoy bastante seguro de que tienes tus razones. :-)
Jens Bradler
@JensBradler Pareces más experto que yo, ¿podrías decirme qué piensas de mi solución? Quiero hacer lo mismo porque ejecuto dos copias de mi sitio web desde dos cuentas en mi ISP: site1.myisp.comy site2.myisp.comsolo responden a su nombre respectivo. Ahora, soy dueño de mi nombre de dominio y me gustaría utilizar el sitio web de mi ISP para equilibrar la carga de mis servidores. ¿No es esa una buena razón? Muchas gracias;)
ncenerar
1
@ncenerar Puede hacerlo, pero esto lo llevará a un solo punto de falla: el equilibrador de carga. Si esto es para el equilibrio de carga (no para la redundancia), también puede utilizar el equilibrio de carga basado en DNS en combinación con la conmutación por error de DNS.
Jens Bradler
2
Esta respuesta refleja el consejo del blog oficial .
Bernard Rosset
28

Tuve el mismo problema y finalmente lo resolví usando dos niveles de proxy. Así es como podría hacer para su situación (creo):

server {
  listen      8001 default_server;
  server_name web1.example.com;
  location / {
    proxy_pass       http://web1.local:80;
    proxy_set_header Host web1.local:80;
  }
}

server {
  listen      8002 default_server;
  server_name web2.example.com;
  location / {
    proxy_pass       http://web2.local:80;
    proxy_set_header Host web2.local:80;
  }
}

server {
  listen      8003 default_server;
  server_name web3.example.com;
  location / {
    proxy_pass       http://web3.local:80;
    proxy_set_header Host web3.local:80;
  }
}

upstream main {
  server 127.0.0.1:8001;
  server 127.0.0.1:8002;
  server 127.0.0.1:8003;
}

server {
  listen      80;
  server_name example.com;
  location / {
    proxy_pass http://main;
  }
}

Como puede ver, el truco es crear un servidor local que responda a un puerto particular que representará el servidor reescribiendo el Host correcto para cada servidor. Luego, puede usar estos servidores locales en su flujo ascendente y finalmente usarlo en el proxy real.

ncenerar
fuente
Originalmente utilicé el enfoque Lua, pero ahora cambié completamente a HAProxy, que me permite hacer exactamente lo que quería con la configuración estándar.
pavel_karoukin
3

Entonces, al leer toda la documentación para nginx (realmente no pude analizar el código para el módulo ascendente = (), se me ocurrió esta solución bastarda. Desafortunadamente, esta solución no realiza un seguimiento de los hosts fallidos, sino que simplemente selecciona uno aleatorio y le dirige la solicitud. Así que tengo que configurar algún tipo de monitoreo para asegurarme de que todos los backends se estén ejecutando.

server {
        listen 80;
        server_name example.com;
        resolver 127.0.0.1;

        location / {
                set $upstream "";
                rewrite_by_lua '
                        local upstreams = {
                                "http://web1.dokku.localdomain",
                                "http://web2.dokku.localdomain",
                                "http://web3.dokku.localdomain",
                                "http://web4.dokku.localdomain"
                        }
                        ngx.var.upstream = upstreams[ math.random( #upstreams ) ] 
                ';
                proxy_pass $upstream;
        }
}
pavel_karoukin
fuente
2

Pasamos el direccionador ascendente como un encabezado separado como este

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            $host;
    proxy_set_header X-Forwarded-For $remote_addr;
    add_header       X-Upstream      $upstream_addr;
  }
}

¿Y si lo intentas?

server {
  listen 80;
  server_name example.com;
  location / {
    proxy_pass       http://main;
    proxy_set_header Host            $upstream_addr;
    proxy_set_header X-Forwarded-For $remote_addr;
    add_header       X-Host          $host;
  }
}
dalore
fuente
2

Si bien el objetivo parece lógico, nginx no va a cambiar el host: encabezado para que coincida con el ascendente . En cambio, trata upstreamlos nombres de dominio como un CNAMEDNS, como una forma de llegar a una dirección IP.

Los encabezados de solicitud (y el cuerpo) se arreglan antes de seleccionar el flujo ascendente. El flujo ascendente puede cambiar a mitad de la solicitud si se encuentra que un flujo ascendente particular no responde, pero la solicitud no cambia.

GreenReaper
fuente
0

Hmm Tengo una configuración similar, en la que simplemente he hecho

location / {
    ... 
    proxy_set_header X-Forwarded-Host $http_host;
    proxy_pass ...;
}

El uso de $http_host(el encabezado de Host HTTP de la solicitud entrante) aquí en lugar de $host(la configuración del nombre de host del servidor) hace que el mismo encabezado de Host pasado por el cliente se pase hacia arriba, en mi prueba.

Consulte también https://stackoverflow.com/questions/14352690/change-host-header-in-nginx-reverse-proxy .

lyngvi
fuente
0

Como otras personas ya publicaron usando la variable de secuencia de comandos (como $ upstream), puede configurarlo de la manera que desee, y eso solucionará el problema, sin piratería de encabezado adicional.

Las variables de script de amenaza del controlador de Proxy Pass de una manera diferente, si un valor no es condicional (no tiene $ en el nombre) se respalda en la fase ascendente en la fase de configuración y se usa más adelante.

Una forma simple de omitir este problema y tener las mayores ventajas de (versión gratuita) en sentido ascendente sería usar algo como Split_Clients:

split_clients $request_uri $my_upstream {
              33%          server1.domainX.com;
              33%          server2.domainX.com;
# Always use DOT at end entry if you wonder why, read the SC code.
              *            server3.domainX.com;  
}
location / {
    ... 
    proxy_pass http://$my_upstream;
}

El ejemplo anterior se ve casi igual que aguas arriba. Existen otros módulos que hacen el mapeo, es decir, chash_map_module , pero como están fuera del árbol, deberá construirlos usted mismo, lo que no es posible para algunos casos de uso /

Mazeryt
fuente