web-dev-qa-db-fra.com

Faire en sorte que nginx passe le nom d'hôte de l'amont lors du reverse proxy

Je lance plusieurs conteneurs Docker avec des noms d'hôtes:

web1.local web2.local web3.local

Le routage vers ceux-ci est effectué en fonction du nom d'hôte par nginx. J'ai un proxy devant cette configuration (sur une autre machine connectée à Internet) où je définis en amont comme:

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

Et la description réelle de l'hôte virtuel:

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

Maintenant, comme les conteneurs reçoivent le nom d'hôte "principal" au lieu de "web1.local", ils ne répondent pas correctement à la demande.

Question: comment puis-je dire à nginx de transmettre le nom du serveur en amont au lieu du nom du groupe de serveurs en amont dans l'hôte: en-tête lors de la demande de mandataire?

94
pavel_karoukin

En fait, vous pouvez le faire via proxy_set_header.

Pour plus de détails, regardez ici: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header ou consultez un exemple de cas d'utilisation ici: https: // stackoverflow .com/questions/12847771/configure-nginx-with-proxy-pass

J'ai inclus l'approche dynamique dans votre configuration postée ci-dessus:

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

Voici un exemple avec un nom d'hôte statique:

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;
  }
}
115
Jens Bradler

J'ai eu le même problème et je l'ai finalement résolu en utilisant deux niveaux de proxy. Voici comment vous pourriez faire pour votre situation (je pense):

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

Comme vous pouvez le voir, l'astuce consiste à créer un serveur local répondant à un port particulier qui procurera un proxy au serveur en réécrivant le bon hôte pour chaque serveur. Ensuite, vous pouvez utiliser ce serveur local dans votre amont et enfin l'utiliser dans le vrai proxy.

28
ncenerar

Donc, en lisant toute la documentation de nginx (je n'ai pas vraiment pu analyser le code du module en amont = (), j'ai trouvé cette solution bâtarde. Malheureusement, cette solution ne garde pas la trace des hôtes défaillants, mais en sélectionne simplement un au hasard et redirige la demande vers lui. Je dois donc configurer une sorte de surveillance pour m'assurer que tous les backends fonctionnent.

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

Nous passons dans l'adrstream en amont comme un en-tête séparé comme celui-ci

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

Et si tu essayais?

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

Bien que l'objectif semble logique, nginx ne va pas changer l'en-tête Host: pour correspondre à l'amont . Au lieu de cela, il traite les noms de domaine upstream comme un CNAME dans DNS - comme un moyen d'accéder à une adresse IP.

Les en-têtes de demande (et le corps) sont fixés avant la sélection de l'amont. L'amont peut changer à mi-demande s'il s'agit d'un amont particulier qui ne répond pas, mais la demande ne change pas.

2
GreenReaper

Hmm. J'ai une configuration similaire, dans laquelle j'ai simplement fait

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

L'utilisation de $http_Host (l'en-tête Hôte HTTP de la demande entrante) ici plutôt que $Host (la configuration du nom d'hôte du serveur) fait passer le même en-tête Host passé par le client à l'amont, dans mes tests.

Voir aussi https://stackoverflow.com/questions/14352690/change-Host-header-in-nginx-reverse-proxy .

2
lyngvi

Comme d'autres personnes ont déjà posté en utilisant une variable de script (comme $ upstream), vous pouvez la définir comme vous le souhaitez, et cela résoudra le problème, sans piratage d'en-tête supplémentaire.

Les variables de script de menace du gestionnaire Proxy Pass d'une manière différente, si une valeur n'est pas conditionnelle (n'a pas $ dans le nom) est sauvegardée en amont lors de la phase de configuration et utilisée ultérieurement.

Un moyen simple d'omettre ce problème et d'avoir le plus d'avantages (version gratuite) en amont serait d'utiliser quelque chose comme 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;
}

L'exemple ci-dessus ressemble presque à l'amont. Il existe d'autres modules qui font le mappage, c'est-à-dire chash_map_module , mais comme ils sont hors de l'arborescence, vous devrez les créer par vous-même, ce qui n'est pas possible pour certains cas d'utilisation /

0
Mazeryt