Steuern des Nginx-Proxy-Ziels mithilfe eines Cookies?

10

Ich versuche, einen Reverse-Proxy mit einem interessanten Apache mod_rewrite-Setup zu konvertieren, um stattdessen Nginx zu verwenden (aufgrund externer Bedenken wechseln wir von Apache zu Nginx, und fast alles funktioniert einwandfrei, außer diesem Teil).

Mein ursprüngliches Setup bestand darin, ein HTTP-Cookie (von einer Anwendung festgelegt) zu lesen und je nach Wert den Reverse-Proxy an verschiedene Backends zu leiten. Es ging ungefähr so:

RewriteCond %{HTTP_COOKIE}  proxy-target-A
RewriteRule ^/original-request/ http://backend-a/some-application [P,QSA]

RewriteCond %{HTTP_COOKIE}  proxy-target-B
RewriteRule ^/original-request http://backend-b/another-application [P,QSA]

RewriteRule ^/original-request http://primary-backend/original-application [P,QSA]

Ich versuche, dasselbe mit Nginx zu erreichen, und meine anfängliche Konfiguration war ungefähr so ​​(wobei "proxy_override" der Name des Cookies ist):

location /original-request {
    if ($cookie_proxy_override = "proxy-target-A") {
        rewrite . http://backend-a/some-application;
        break;
    }
    if ($cookie_proxy_override = "proxy-target-B") {
        rewrite . http://backend-b/another-application;
        break;
    }
    proxy_pass http://primary-backend/original-application;
}

Aber es war nicht so. Ich habe versucht zu sehen, ob Nginx mein Cookie lesen kann, indem ich den primären Proxy schreibe, um zu etwas weiterzuleiten, das auf basiert, ${cookie_proxy_override}und ich kann sehen, dass es den Inhalt gut liest, aber die ifs scheinen immer fehlzuschlagen.

Mein nächster Versuch laut Rikihs Antwort war folgender:

location /original-request {
    if ($http_cookie ~ "proxy-target-A") {
        rewrite . http://backend-a/some-application;
        break;
    }
    if ($http_cookie ~ "proxy-target-B") {
        rewrite . http://backend-b/another-application;
        break;
    }
    proxy_pass http://primary-backend/original-application;
}

Und jetzt kann ich sehen, dass der ifBlock aktiviert wird, aber anstatt die Anfrage zu proxyieren (wie ich es mir vorgestellt hatte), wird eine 302-Umleitung an die angegebene URL zurückgegeben - was ich nicht versuche: Ich brauche den Server um die Anforderung transparent an die Backends weiterzuleiten und die Antwort an den ursprünglichen Client weiterzuleiten.

Was mache ich falsch?

Guss
quelle

Antworten:

16

Ähnlich wie bei dieser Antwort . Nginx 'idiomatische Herangehensweise an diese Art von Problemen erfolgt über map.

Grundsätzlich definieren Sie einen mapIn- httpAbschnitt

map $cookie_proxy_override $my_upstream {
  default default-server-or-upstream;
  ~^(?P<name>[\w-]+) $name;
}

Dann verwenden Sie einfach $my_upstreamin locationAbschnitt (en):

location /original-request {
  proxy_pass http://$my_upstream$uri;
}

Nginx wertet Kartenvariablen nur einmal (pro Anfrage) und bei Verwendung träge aus.

Alexander Azarov
quelle
3
Vielen Dank, das ist ein besserer Ansatz als meiner, vor allem, weil ich die benannte Cookie-Variable direkt verwenden kann (nicht sicher, warum ich nicht einsteigen kann if) und sie implementiert habe. Es gibt jedoch ein Problem: Nginx (zumindest meine Version: 1.0.0) mag keine nummerierten Captures map, daher musste ich ~^(?P<name>[\w-]+) $name;stattdessen verwenden. Ich habe Ihre Antwort entsprechend bearbeitet.
Guss
3

Schließlich läuft meine Lösung darauf hinaus:

server {
    ...
    set $upstream "default-server-or-upstream";
    if ($http_cookie ~ "proxy_override=([\w-]+)") {
        set $upstream $1;                                   
    }

    location /original-request {
        proxy_pass http://$upstream/original-application
    }
}

Der Test wird im serverBereich für jede Anforderung durchgeführt (bevor die eigentliche Umleitung aufgelöst wird) und wird nur zum Festlegen einer Variablen verwendet - dies ist anscheinend eine unterstützte Verwendung des Nginx-Moduls "Umschreiben". Es testet auch das $http_cookieGanze wie von @Rikih vorgeschlagen, enthält jedoch den Namen des Cookies, um sicherzustellen, dass ich nicht mit zufälligen Dingen übereinstimme, die Leute möglicherweise auf mich werfen.

In dem locationBereich, in dem ich die Umleitung durchführen möchte, verwende ich dann den Variablennamen, der entweder die Standard-Upstream-Konfiguration enthält oder vom Cookie überschrieben wurde.

Guss
quelle
0

Hast du $ http_cookie ausprobiert? http://wiki.nginx.org/HttpRewriteModule

if ($ http_cookie ~ * "proxy-target-A") {foo; }}

Chocripple
quelle
Das hat in der Tat für den Test funktioniert, obwohl ich nicht sicher bin, warum ich nicht einfach gegen den spezifischen Cookie-Namen testen kann. Was mir nicht gefallen hat, ist, dass rewritekein Proxy-Rewrite durchgeführt wird, sondern eine Umleitung an den Client zurückgegeben wird, und ich kann proxy_pass im ifBlock nicht verwenden . Ich habe die Frage entsprechend aktualisiert.
Guss
0

Ich habe ein Beispiel, das ich verwende, um den Anforderungsheader basierend auf udid zu erkennen, und es funktioniert. Vielleicht bekommen Sie eine Idee.

   location / {
      proxy_set_header Host $http_host;
  if ($request_uri ~ ^/(.*)udid=xxxxxxxxxxxxxx(.*)$) {
    proxy_pass   http://1.1.1.1$request_uri;
    break;
  }
  if ($request_uri ~ ^/(.*)udid=yyyyyyyyyyyyyy(.*)$) {
    proxy_pass   http://3.3.3.3$request_uri;
    break;
  }
       proxy_pass http://2.2.2.2$request_uri;
    }
Chocripple
quelle
Welche Version von Nginx verwenden Sie? Ich verwende 1.0 und wenn ich proxy_pass verwende, wie Sie hier angegeben haben, erhalte ich folgende Fehlermeldung:nginx: [emerg] "proxy_pass" may not have URI part in location given by regular expression, or inside named location, or inside the "if" statement, or inside the "limit_except" block in /etc/nginx/conf.d/proxy.conf:47
Guss
Ich benutze Nginx-0.8.53-1.el5
Chocripple
Vielleicht möchten Sie einen Blick auf forum.nginx.org/read.php?2,13955,15981
chocripple
Die Lösung im Forum besteht darin, den Anforderungs-URI beim Proxys auf einen anderen Server nicht zu ändern, aber genau das muss ich tun - schreiben Sie den Anforderungs-URI neu, um auf eine Anwendung abzuzielen, die sich von der ursprünglichen URL unterscheidet. Außerdem scheint Ihr Beispiel auch den Anforderungs-URI im proxy_passBefehl zu verwenden, daher bin ich mir nicht sicher, wie er angesichts der obigen Forumsdiskussion für Sie funktionieren kann.
Guss