¿Controlando el objetivo del proxy Nginx usando una cookie?

11

Estoy tratando de convertir un proxy inverso usando una configuración interesante de Apache mod_rewrite para usar Nginx en su lugar (debido a preocupaciones externas, nos estamos moviendo de Apache a Nginx, y casi todo funciona bien, excepto esta parte).

Mi configuración original era leer una cookie HTTP (establecida por alguna aplicación) y, dependiendo de su valor, dirigir el proxy inverso a diferentes backends. Fue algo como ésto:

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]

Estoy tratando de lograr lo mismo usando Nginx, y mi configuración inicial fue algo así (donde "proxy_override" es el nombre de la 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;
}

Pero no fue así. Traté de ver si Nginx puede leer mi cookie escribiendo el proxy primario para redirigir a algo basado ${cookie_proxy_override}y puedo ver que lee bien el contenido, pero ifparece que los s siempre fallan.

Mi siguiente intento, de acuerdo con la respuesta de Rikih fue este:

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

Y ahora puedo ver que el ifbloque se activa, pero en lugar de enviar la solicitud (como pensé que haría), devuelve una redirección 302 a la URL especificada, que no es lo que estoy tratando de hacer: necesito el servidor para retransmitir de forma transparente la solicitud a los servidores y canalizar la respuesta al cliente original.

¿Qué estoy haciendo mal?

Guss
fuente

Respuestas:

16

Similar a esta respuesta . El enfoque idiomático de Nginx para este tipo de problemas es vía map.

Básicamente, se define una mapen httpla sección

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

Luego, simplemente use $my_upstreamen la location(s) sección (es):

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

Nginx evalúa las variables del mapa perezosamente, solo una vez (por solicitud) y cuando las está utilizando.

Alexander Azarov
fuente
3
Gracias, ese es un mejor enfoque que el mío, sobre todo porque puedo usar la variable de cookie nombrada directamente (no estoy seguro de por qué no puedo ifentrar) y la implementé. Sin embargo, hay un problema: a Nginx (al menos mi versión: 1.0.0) no le gustan las capturas numeradas map, así que tuve que usarlas ~^(?P<name>[\w-]+) $name;. He editado tu respuesta en consecuencia.
Guss
3

Finalmente, mi solución se reduce a esto:

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

La prueba se realiza en el serverámbito de cada solicitud (antes de que se resuelva la redirección real) y solo se usa para establecer una variable; aparentemente, este es un uso compatible del módulo Nginx "rewrite". También prueba todo $http_cookiecomo sugirió @Rikih, pero incluye el nombre de la cookie para asegurarse de que no coincida con cosas aleatorias que la gente podría estar arrojándome.

Luego, en el locationámbito donde quiero hacer la redirección, utilizo el nombre de la variable que contiene la configuración ascendente predeterminada o la cookie la sobrescribió.

Guss
fuente
0

¿Has probado $ http_cookie? http://wiki.nginx.org/HttpRewriteModule

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

chocripple
fuente
De hecho, eso funcionó para la prueba, aunque no estoy seguro de por qué no puedo simplemente probar el nombre de la cookie específica. Lo que no me gustó es que en rewriterealidad no hace una reescritura de proxy, sino que devuelve una redirección al cliente, y no puedo usar proxy_pass en el ifbloque. He actualizado la pregunta en consecuencia.
Guss
0

Tengo una muestra que uso para detectar el encabezado de solicitud basado en udid y está funcionando, podría ser que tengas alguna idea.

   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
fuente
¿Qué versión de Nginx estás usando? Estoy usando 1.0 y cuando uso proxy_pass como usted especificó aquí, recibo este mensaje de error: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
uso nginx-0.8.53-1.el5
chocripple
tal vez quieras echar un vistazo forum.nginx.org/read.php?2,13955,15981
chocripple
La solución en el foro es no cambiar el URI de la solicitud al enviar proxy a otro servidor, pero eso es exactamente lo que necesito hacer: reescribir el URI de la solicitud para apuntar a una aplicación diferente de la que incluye la URL original. Además, su ejemplo también parece usar el URI de solicitud en el proxy_passcomando, por lo que no estoy seguro de cómo puede funcionar para usted dado el debate del foro anterior.
Guss