Kontrolujesz docelowy serwer proxy Nginx za pomocą pliku cookie?

11

Próbuję przekonwertować zwrotny serwer proxy za pomocą ciekawej konfiguracji mod_rewrite Apache, aby zamiast tego używać Nginx (z powodu problemów zewnętrznych przechodzimy z Apache na Nginx, a większość wszystkiego działa dobrze oprócz tej części).

Moją pierwotną konfiguracją było odczytanie pliku cookie HTTP (ustawionego przez jakąś aplikację) i zależnie od jego wartości, skierować zwrotne proxy do różnych backendów. Poszło mniej więcej tak:

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]

Próbuję osiągnąć to samo za pomocą Nginx, a moja początkowa konfiguracja była mniej więcej taka (gdzie „proxy_override” to nazwa pliku cookie):

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

Ale tak nie było. Próbowałem sprawdzić, czy Nginx może odczytać mój plik cookie, pisząc główny serwer proxy, aby przekierować do czegoś opartego na, ${cookie_proxy_override}i widzę, że dobrze czyta treść, ale ifwydaje się, że zawsze zawodzi.

Moja następna próba, zgodnie z odpowiedzią Rikih, była następująca:

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

A teraz widzę, że ifblok zostaje aktywowany, ale zamiast proxy żądania (tak jak myślałem, że tak zrobi), zwraca przekierowanie 302 do podanego adresu URL - co nie jest tym, co próbuję zrobić: potrzebuję serwera w celu transparentnego przekazania żądania do backendów i przesłania odpowiedzi do oryginalnego klienta.

Co ja robię źle?

Guss
źródło

Odpowiedzi:

16

Podobne do tej odpowiedzi . Idiomatyczne podejście Nginx do tego rodzaju problemów odbywa się za pośrednictwem map.

Zasadniczo definiujesz sekcję „ mapin http

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

Następnie wystarczy użyć $my_upstreamw locationsekcjach:

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

Nginx leniwie ocenia zmienne mapy, tylko raz (na żądanie) i kiedy ich używasz.

Aleksander Azarow
źródło
3
Dzięki, jest to lepsze podejście niż moje, szczególnie dlatego, że mogę bezpośrednio używać nazwanej zmiennej cookie (nie jestem pewien, dlaczego nie mogę wejść if) i zaimplementowałem ją. Jest jednak jeden problem - Nginx (przynajmniej moja wersja: 1.0.0) nie lubi przechwytywania numerowanego map, więc musiałem go użyć ~^(?P<name>[\w-]+) $name;. Odpowiednio zredagowałem twoją odpowiedź.
Guss,
3

W końcu moje rozwiązanie sprowadza się do tego:

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

Test jest wykonywany w serverzakresie dla każdego żądania (przed rozwiązaniem rzeczywistego przekierowania) i jest po prostu używany do ustawienia zmiennej - jest to najwyraźniej obsługiwane użycie modułu „przepisywania” Nginx. $http_cookieTestuje także całość, jak sugeruje @Rikih, ale zawiera nazwę pliku cookie, aby upewnić się, że nie pasuję do losowych rzeczy, które ludzie mogą mi rzucać.

Następnie w locationzakresie, w którym chcę dokonać przekierowania, używam nazwy zmiennej, która zawiera domyślną konfigurację wysyłania lub została zastąpiona przez plik cookie.

Guss
źródło
0

próbowałeś $ http_cookie? http://wiki.nginx.org/HttpRewriteModule

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

chocripple
źródło
To rzeczywiście zadziałało w teście, chociaż nie jestem pewien, dlaczego nie mogę po prostu przetestować konkretnej nazwy pliku cookie. Nie podobało mi się to, że rewritetak naprawdę nie przepisuje proxy, ale zamiast tego zwraca przekierowanie do klienta i nie mogę użyć proxy_pass w ifbloku. Odpowiednio zaktualizowałem pytanie.
Guss
0

Mam próbkę, której używam do wykrywania nagłówka żądania na podstawie udid i działa, być może dostaniesz jakiś pomysł.

   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
źródło
Z jakiej wersji Nginx korzystasz? Korzystam z wersji 1.0 i gdy używam proxy_pass, jak podano tutaj, pojawia się następujący komunikat o błędzie: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
używam nginx-0.8.53-1.el5
chocripple
może chcesz zajrzeć na forum.nginx.org/read.php?2,13955,15981
chocripple
Rozwiązaniem na forum jest to, aby nie zmieniać identyfikatora URI żądania podczas proxy do innego serwera, ale to jest dokładnie to, co muszę zrobić - przepisać identyfikator URI żądania, aby celować w aplikację inną niż ta, którą zawiera oryginalny adres URL. W twoim przykładzie również zdaje się używać identyfikatora URI żądania w proxy_passpoleceniu, więc nie jestem pewien, w jaki sposób może on działać w przypadku powyższej dyskusji na forum.
Guss