Contrôler la cible proxy Nginx à l'aide d'un cookie?

11

J'essaie de convertir un proxy inverse en utilisant une configuration Apache mod_rewrite intéressante pour utiliser Nginx à la place (en raison de problèmes externes, nous passons d'Apache à Nginx, et la plupart des choses fonctionnent bien sauf cette partie).

Ma configuration d'origine était de lire un cookie HTTP (défini par une application) et, en fonction de sa valeur, de diriger le proxy inverse vers différents backends. Il est allé quelque chose comme ça:

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]

J'essaie d'obtenir la même chose en utilisant Nginx, et ma configuration initiale était quelque chose comme ça (où "proxy_override" est le nom du 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;
}

Mais ce n'était pas le cas. J'ai essayé de voir si Nginx peut lire mon cookie en écrivant le proxy principal pour rediriger vers quelque chose basé sur ${cookie_proxy_override}et je peux voir qu'il lit bien le contenu, mais les ifs semblent toujours échouer.

Mon prochain essai, selon la réponse de Rikih, était le suivant:

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

Et maintenant, je peux voir que le ifbloc est activé, mais au lieu de mandater la demande (comme je le pensais), il renvoie une redirection 302 vers l'URL spécifiée - ce qui n'est pas ce que j'essaie de faire: j'ai besoin du serveur pour relayer de manière transparente la demande aux backends et diriger la réponse vers le client d'origine.

Qu'est-ce que je fais mal?

Guss
la source

Réponses:

16

Similaire à cette réponse . L'approche idiomatique de Nginx à ce type de problèmes est via map.

Fondamentalement, vous définissez une section mapinhttp

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

Ensuite, vous utilisez simplement $my_upstreamdans la locationsection (s):

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

Nginx évalue les variables de carte paresseusement, une seule fois (par demande) et lorsque vous les utilisez.

Alexander Azarov
la source
3
Merci, c'est une meilleure approche que la mienne, notamment parce que je peux utiliser directement la variable de cookie nommée (je ne sais pas pourquoi je ne peux pas if) et je l'ai implémentée. Il y a un problème cependant - Nginx (au moins ma version: 1.0.0) n'aime pas les captures numérotées map, j'ai donc dû utiliser à la ~^(?P<name>[\w-]+) $name;place. J'ai modifié votre réponse en conséquence.
Guss
3

Finalement, ma solution se résume à ceci:

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

Le test est effectué dans la serverportée de chaque demande (avant que la redirection réelle ne soit résolue) et est juste utilisé pour définir une variable - il s'agit apparemment d'une utilisation prise en charge du module de «réécriture» de Nginx. Il teste également l'intégralité $http_cookiecomme l'a suggéré @Rikih, mais inclut le nom du cookie pour m'assurer que je ne correspond pas aux éléments aléatoires que les gens pourraient me lancer.

Ensuite, dans la locationportée où je veux faire la redirection, j'utilise le nom de variable qui contient la configuration en amont par défaut ou a été remplacé par le cookie.

Guss
la source
0

avez-vous essayé $ http_cookie? http://wiki.nginx.org/HttpRewriteModule

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

chocripple
la source
Cela a en effet fonctionné pour le test, mais je ne sais pas pourquoi je ne peux pas simplement tester par rapport au nom de cookie spécifique. Ce que je n'ai pas aimé, c'est que rewritecela ne fait pas de réécriture de proxy mais renvoie à la place une redirection vers le client, et je ne peux pas utiliser proxy_pass dans le ifbloc. J'ai mis à jour la question en conséquence.
Guss
0

J'ai un échantillon que j'utilise pour détecter l'en-tête de demande basé sur udid et cela fonctionne, vous aurez peut-être une idée.

   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
la source
Quelle version de Nginx utilisez-vous? J'utilise 1.0 et lorsque j'utilise proxy_pass comme vous l'avez spécifié ici, je reçois ce message d'erreur: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
j'utilise nginx-0.8.53-1.el5
chocripple
peut-être que vous voulez jeter un oeil forum.nginx.org/read.php?2,13955,15981
chocripple
La solution dans le forum est de ne pas modifier l'URI de la demande lors de la procuration à un autre serveur, mais c'est exactement ce que je dois faire - réécrire l'URI de la demande pour cibler une application différente de ce que l'URL d'origine comprend. En outre, votre exemple semble également utiliser l'URI de demande dans la proxy_passcommande, donc je ne sais pas comment cela peut fonctionner pour vous compte tenu de la discussion sur le forum ci-dessus.
Guss