Faire que nginx passe le nom d’hôte de l’amont lors de la procuration inverse

89

J'exécute plusieurs conteneurs Docker avec des noms d'hôte:

web1.local web2.local web3.local

Routage vers ceux-ci effectué en fonction du nom d'hôte par nginx. J'ai un proxy devant cette configuration (sur une machine différente 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 description de l’hôte virtuel:

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

À présent, les conteneurs recevant 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 Host: en-tête lors de la demande de proxy?

pavel_karoukin
la source
3
Je ne pense pas que tu peux. Pourquoi ne configurez-vous pas vos serveurs principaux pour répondre à main ou example.com? Ce n'est pas comme si le serveur ne savait pas qui c'était . L'inverse est facilement possible: proxy_set_header Host $ host; remplacera toute variable d’hôte provenant d’amont par le nom d’hôte de la demande initiale.
Andrew Domaszek
La bonne chose à faire est de réparer l'application.
Michael Hampton

Réponses:

109

En fait, vous pouvez le faire via proxy_set_header.

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

J'ai inclus l'approche dynamique dans la configuration publié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;
  }
}
Jens Bradler
la source
7
proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for; semble mieux
sivann
1
@pavel: compris. En fait, j'ai aussi fait des recherches et des tests. Il semble qu’il n’y ait pas d’approche directe pour répondre à vos besoins. Donc, même une solution "bâtarde" est une solution. Je n'aime pas demander pourquoi tu aimerais faire ça. Je suis sûr que tu as tes raisons. :-)
Jens Bradler
@JensBradler Vous semblez plus expert que moi, pouvez-vous me dire ce que vous pensez de ma solution? Je veux faire la même chose parce que je lance deux copies de mon site Web à partir de deux comptes sur mon FAI: site1.myisp.comet site2.myisp.comet ils ne répondent qu'à leur nom respectif. Je suis maintenant le propriétaire de mon nom de domaine et j'aimerais utiliser le site Web de mon fournisseur de services Internet pour équilibrer la charge de mes serveurs. N'est-ce pas une bonne raison? Merci beaucoup;)
ncenerar
1
@ncenerar Vous pouvez le faire, mais cela vous mènera à un seul point d'échec: l'équilibreur de charge. S'il s'agit d'un équilibrage de charge (et non de redondance), vous pouvez également utiliser un équilibrage de charge basé sur DNS en combinaison avec un basculement DNS.
Jens Bradler
2
Cette réponse reflète les conseils du blog officiel .
Bernard Rosset
28

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 constater, le truc 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 ces serveurs locaux dans votre amont et enfin utiliser cela en amont dans le proxy réel.

ncenerar
la source
À l’origine, j’avais utilisé l’approche Lua, mais je suis maintenant complètement passé à HAProxy, ce qui me permet de faire exactement ce que je voulais avec une configuration standard.
pavel_karoukin
3

Donc, en lisant toute la documentation de nginx (je ne pouvais pas vraiment analyser le code du module en amont = ()), je trouvais cette solution bâtarde. Malheureusement, cette solution ne garde pas la trace des hôtes en échec, mais sélectionne simplement un hôte aléatoire et redirige sa demande. 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;
        }
}
pavel_karoukin
la source
2

Nous passons dans l'add 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;
  }
}
Dalore
la source
2

Bien que l'objectif semble logique, nginx ne changera pas l'en-tête Host: pour qu'il corresponde à l'amont . Au lieu de cela, il traite upstreamles noms de domaine comme CNAMEdans un DNS - comme un moyen d'obtenir une adresse IP.

Les en-têtes de requête (et le corps) sont fixés avant la sélection de l'amont. Le processus amont peut changer à mi-demande s'il s'avère qu'un amont particulier ne répond pas, mais la demande ne change pas.

GreenReaper
la source
0

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 HTTP Host de la requête entrante) ici plutôt que $host(la configuration du nom d'hôte du serveur) provoque la transmission du même en-tête Host par le client en amont, lors de mes tests.

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

Lyngvi
la source
0

Comme d'autres personnes ont déjà posté à l'aide d'une variable de script (telle que $ en amont), vous pouvez la définir comme vous le souhaitez, ce qui résoudra le problème, sans piratage supplémentaire de l'en-tête.

Les variables de script de menace du gestionnaire de proxy Proxy Pass différemment, si une valeur n'est pas conditionnelle (le nom ne contient pas $) est sauvegardé en amont lors de la phase de configuration et utilisé ultérieurement.

Un moyen simple d'omettre ce problème, et qui présente le plus d'avantages de (version gratuite) en amont, consisterait à 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’arbre, vous devrez les construire vous-même, ce qui n’est pas possible pour certains cas d’utilisation /

Mazeryt
la source