154 votes

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

J'exécute plusieurs conteneurs docker avec des noms d'hôtes :

web1.local web2.local web3.local

Le routage vers ces derniers est effectué par nginx sur la base du nom d'hôte. J'ai un proxy en amont de cette configuration (sur une autre machine connectée à Internet) où je définis l'amont comme :

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

Et la description de l'hôte virtuel actuel :

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

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

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

3 votes

Je ne pense pas que tu puisses. Pourquoi ne pas configurer vos serveurs dorsaux pour qu'ils répondent à main ou example.com ? Ce n'est pas comme si le backend ne savait pas qui est le serveur principal. il est. L'inverse est tout à fait possible : proxy_set_header Host $host ; remplacera toute variable Host revenant de l'amont par le nom d'hôte de la requête originale.

0 votes

La chose à faire est de réparer l'application.

4 votes

@MichaelHampton Ce n'est pas possible dans certains cas, par exemple si l'on utilise des proxy_ssl_server_name pour TLS SNI, il faut le bon nom de serveur.

170voto

Jens Bradler Points 5913

En fait, vous pouvez le faire via proxy_set_header.

Pour plus de détails, cliquez ici : http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header ou voyez 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 affiché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;
  }
}

10 votes

Proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; semble meilleur

2 votes

@pavel : compris. En fait, j'ai aussi fait quelques recherches et quelques tests. Il semble qu'il n'y ait pas d'approche directe pour répondre à votre exigence. Donc même une solution "abâtardie" est une solution. Je n'aime pas demander pourquoi vous voulez faire ça. Je suis sûr que vous avez vos raisons :-)

0 votes

@JensBradler Vous semblez plus expert que moi donc, pouvez-vous me dire ce que vous pensez de ma solution ? Je veux faire la même chose car je gère deux copies de mon site web à partir de deux comptes sur mon FAI : site1.myisp.com y site2.myisp.com et ils ne répondent qu'à leur nom respectif. Je suis maintenant propriétaire de mon nom de domaine et j'aimerais utiliser le site Web de mon fournisseur d'accès pour équilibrer la charge de mes serveurs. N'est-ce pas une bonne raison ? Merci beaucoup ;)

38voto

Rahul Premraj Points 424

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 va servir de proxy au serveur en réécrivant le bon Host pour chaque serveur. Ensuite, vous pouvez utiliser ces serveurs locaux dans votre upstream et enfin utiliser cet upstream dans le vrai proxy.

1 votes

J'ai d'abord utilisé l'approche Lua, mais je suis maintenant passé complètement à HAProxy qui permet de faire exactement ce que je voulais avec une configuration standard.

0 votes

Je me demande comment cela affecte les performances (ajouter une server { .. proxy_pass ... } couche)

0 votes

C'est utile si vous voulez utiliser un résolveur avec plus d'un serveur en amont, parce que la version gratuite de nginx ne permet pas de résoudre en amont (utiliser une variable comme nom de proxy n'est pas suffisant, parce qu'il y a plus d'un point de terminaison, dans le cas où nginx agit comme un équilibreur de charge).

10voto

GreenReaper Points 351

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

Les en-têtes de la demande (et le corps) sont fixés avant que l'amont ne soit sélectionné. L'amont peut changer au milieu de la demande si un amont particulier ne répond pas, mais la demande ne change pas.

5voto

dalore Points 141

Nous passons l'adresse amont dans un en-tête séparé comme ceci

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 vous essayiez ?

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

2 votes

Non. Au moment où "proxy_set_header" est évalué, le $upstream_addr est toujours nul, il est choisi plus tard.

3voto

pavel_karoukin Points 1451

Après avoir lu toute la documentation de nginx (je n'ai pas pu analyser le code du module amont =( ), j'ai trouvé cette solution bâtarde. Malheureusement, cette solution ne garde pas trace des hôtes qui ont échoué, mais en choisit simplement un au hasard et redirige la demande vers lui. Je dois donc mettre en place 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;
        }
}

2 votes

Que se passe-t-il si un hôte en amont est en panne avec cette méthode ?

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X