Lassen Sie nginx beim Reverseproxying den Hostnamen des Upstreams übergeben

89

Ich führe mehrere Docker-Container mit Hostnamen aus:

web1.local web2.local web3.local

Das Routing zu diesen erfolgt basierend auf dem Hostnamen von nginx. Ich habe einen Proxy vor diesem Setup (auf einem anderen Computer, der mit dem Internet verbunden ist), den ich als Upstream definiere:

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

Und die tatsächliche Beschreibung des virtuellen Hosts:

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

Da Container nun den Hostnamen "main" anstelle von "web1.local" erhalten, reagieren sie nicht richtig auf die Anforderung.

Frage: Wie kann ich nginx anweisen, den Namen des Upstream-Servers anstelle des Namens der Upstream-Servergruppe im Host: -Header zu übergeben, wenn eine Anfrage als Proxy gesendet wird?

pavel_karoukin
quelle
3
Ich glaube nicht, dass du kannst. Warum stellen Sie Ihre Backend-Server nicht so ein, dass sie auf main oder example.com antworten? Es ist nicht so, dass das Backend nicht weiß, wer es ist. Das Gegenteil ist ohne weiteres möglich: proxy_set_header Host $ host; ersetzt jede vom Upstream zurückkommende Hostvariable durch den Hostnamen aus der ursprünglichen Anforderung.
Andrew Domaszek
Das Richtige ist, die Anwendung zu reparieren.
Michael Hampton

Antworten:

109

Eigentlich kannst du das über proxy_set_header machen.

Weitere Informationen finden Sie hier: http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header oder in einem Anwendungsfall hier: https://stackoverflow.com/questions/12847771/configure-nginx- with-proxy-pass

Ich habe den dynamischen Ansatz in Ihre oben veröffentlichte Konfiguration aufgenommen:

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

Hier ist ein Beispiel mit einem statischen Hostnamen:

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
quelle
7
proxy_set_header X-Forwarded-For $ proxy_add_x_forwarded_for; scheint besser
sivann
1
@pavel: Verstanden. Eigentlich habe ich auch recherchiert und ein paar Tests gemacht. Es scheint, dass es keinen direkten Ansatz gibt, um Ihre Anforderungen zu erfüllen. Also ist auch eine "bastardisierte" Lösung eine Lösung. Ich möchte nicht fragen, warum Sie das tun möchten. Ich bin mir ziemlich sicher, dass Sie Ihre Gründe haben. :-)
Jens Bradler
@JensBradler Du scheinst also kompetenter zu sein als ich, könntest du mir sagen, was du von meiner Lösung hältst? Ich möchte dasselbe tun, weil ich zwei Kopien meiner Website von zwei Konten auf meinem ISP aus betreibe: site1.myisp.comund site2.myisp.comund sie nur auf ihren jeweiligen Namen antworten. Ich besitze jetzt meinen Domainnamen und möchte meine ISP-Website zum Lastenausgleich meiner Server verwenden. Ist das nicht ein guter Grund? Vielen Dank;)
ncenerar
1
@ncenerar Sie können dies tun, aber dies bringt Sie zu einer einzigen Fehlerquelle: dem Load Balancer. Wenn dies zum Lastenausgleich (nicht zur Redundanz) dient, können Sie auch den DNS-basierten Lastenausgleich in Kombination mit dem DNS-Failover verwenden.
Jens Bradler
2
Diese Antwort spiegelt den Rat des offiziellen Blogs wider .
Bernard Rosset
28

Ich hatte das gleiche Problem und habe es schließlich mit zwei Proxy-Ebenen gelöst. Hier ist, wie Sie für Ihre Situation tun könnten (denke ich):

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

Wie Sie sehen, besteht der Trick darin, einen lokalen Server zu erstellen, der auf einen bestimmten Port reagiert, der den Server per Proxy überträgt, indem für jeden Server der richtige Host neu geschrieben wird. Dann können Sie diese lokalen Server in Ihrem Upstream verwenden und schließlich diesen Upstream im realen Proxy verwenden.

ncenerar
quelle
Ich habe ursprünglich den Lua-Ansatz verwendet, bin jetzt aber vollständig auf HAProxy umgestiegen, wodurch ich mit der Standardkonfiguration genau das tun kann, was ich wollte.
pavel_karoukin
3

Nachdem ich die gesamte Dokumentation für nginx gelesen hatte (ich konnte den Code für das vorgelagerte Modul nicht wirklich analysieren = ()), kam ich zu dieser bastardisierten Lösung. Leider verfolgt diese Lösung nicht die ausgefallenen Hosts, sondern wählt einfach eine zufällige aus und leitet die Anforderung an diese weiter. Ich muss also eine Art Überwachung einrichten, um sicherzustellen, dass alle Backends ausgeführt werden.

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
quelle
2

Wir übergeben die Upstream-Adresse wie folgt als separaten Header

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

Was wäre, wenn Sie es versuchen würden?

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
quelle
2

Während das Ziel logisch erscheint, wird Nginx den Host: -Header nicht an den Upstream anpassen . Stattdessen werden upstreamDomänennamen wie CNAMEin DNS behandelt - als Möglichkeit, an eine IP-Adresse zu gelangen.

Die Anforderungsheader (und der Hauptteil) werden festgelegt, bevor der Upstream ausgewählt wird. Der Upstream ändert sich möglicherweise während der Anforderung, wenn festgestellt wird, dass ein bestimmter Upstream nicht reagiert, die Anforderung ändert sich jedoch nicht.

GreenReaper
quelle
0

Hmm. Ich habe ein ähnliches Setup, in dem ich es einfach gemacht habe

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

Die Verwendung von $http_host(dem HTTP-Host-Header aus der eingehenden Anforderung) anstelle von $host(der Konfiguration des Server-Hostnamens) bewirkt, dass derselbe Host-Header, der vom Client übergeben wurde, in meinen Tests an den Upstream übergeben wird.

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

lyngvi
quelle
0

Wie andere Leute bereits mit Skriptvariablen (wie $ upstream) gepostet haben, können Sie diese so einstellen, wie Sie möchten, und das behebt das Problem ohne zusätzliches Header-Hacking.

Proxy Pass-Handler-Bedrohungsskriptvariablen werden auf andere Weise, wenn ein Wert nicht bedingt ist (nicht $ im Namen hat), in der Konfigurationsphase auf den Upstream gesichert und später verwendet.

Eine einfache Möglichkeit, dieses Problem auszulassen und die meisten Vorteile der (kostenlosen) Upstream-Version zu nutzen, wäre die Verwendung von 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;
}

Das obige Beispiel sieht fast so aus wie das vorgelagerte. Es gibt andere Module, die das Mapping durchführen, dh chash_map_module , aber da sie nicht in der Baumstruktur enthalten sind, müssen Sie sie selbst erstellen, was für einige Anwendungsfälle nicht möglich ist.

Mazeryt
quelle