Controlando o destino do proxy Nginx usando um cookie?

11

Estou tentando converter um proxy reverso usando uma configuração interessante do mod_rewrite do Apache para usar o Nginx (devido a preocupações externas, estamos passando do Apache para o Nginx, e quase tudo funciona bem, exceto esta parte).

Minha configuração original era ler um cookie HTTP (definido por algum aplicativo) e, dependendo do seu valor, direcionar o proxy reverso para diferentes back-ends. Aconteceu mais ou menos assim:

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]

Estou tentando conseguir o mesmo usando o Nginx, e minha configuração inicial era algo assim (onde "proxy_override" é o nome do 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;
}

Mas isso não aconteceu. Eu tentei ver se o Nginx pode ler meu cookie escrevendo o proxy primário para redirecionar para algo baseado ${cookie_proxy_override}e posso ver que ele lê bem o conteúdo, mas os ifsempre parecem falhar.

Minha próxima tentativa, de acordo com a resposta de Rikih, foi esta:

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

E agora posso ver que o ifbloco é ativado, mas, em vez de proxies a solicitação (como eu pensava que faria), ele retorna um redirecionamento 302 para o URL especificado - o que não é o que estou tentando fazer: preciso do servidor retransmitir de forma transparente a solicitação para os back-end e canalizar a resposta para o cliente original.

O que estou fazendo errado?

Guss
fonte

Respostas:

16

Semelhante a esta resposta . A abordagem idiomática do Nginx para esse tipo de problema é via map.

Basicamente, você define uma mapem httpseção

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

Então você simplesmente usar $my_upstreamna location(s) secção:

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

O Nginx avalia variáveis ​​de mapa preguiçosamente, apenas uma vez (por solicitação) e quando você as estiver usando.

Alexander Azarov
fonte
3
Obrigado, essa é uma abordagem melhor que a minha, principalmente porque eu posso usar a variável de cookie nomeada diretamente (não sei por que não posso entrar if) e a implementei. Porém, há um problema: o Nginx (pelo menos minha versão: 1.0.0) não gosta de capturas numeradas map, então tive que usá-lo ~^(?P<name>[\w-]+) $name;. Editei sua resposta de acordo.
Guss
3

Eventualmente, minha solução se resume a isso:

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

O teste é realizado no serverescopo de cada solicitação (antes que o redirecionamento real seja resolvido) e é usado apenas para definir uma variável - esse é aparentemente um uso suportado do módulo "reescrita" do Nginx. Ele também testa todo o $http_cookieconteúdo, como o @Rikih sugeriu, mas inclui o nome do cookie para garantir que eu não corresponda a coisas aleatórias que as pessoas possam estar jogando para mim.

Em seguida, no locationescopo em que desejo redirecionar, utilizo o nome da variável que contém a configuração upstream padrão ou foi substituída pelo cookie.

Guss
fonte
0

você experimentou $ http_cookie? http://wiki.nginx.org/HttpRewriteModule

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

chocripple
fonte
Isso realmente funcionou para o teste, embora eu não tenha certeza do motivo pelo qual não posso apenas testar o nome do cookie específico. O que eu não gostei é que rewrite, na verdade, ele não reescreve o proxy, mas retorna um redirecionamento para o cliente e não posso usar proxy_pass no ifbloco. Eu atualizei a pergunta de acordo.
Guss
0

Eu tenho um exemplo que eu uso para detectar o cabeçalho da solicitação com base no udid e está funcionando, pode ser que você tenha uma idéia.

   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
fonte
Qual versão do Nginx você está usando? Estou usando 1.0 e quando uso proxy_pass como você especificou aqui, recebo esta mensagem de erro: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
eu uso nginx-0.8.53-1.el5
chocripple
pode ser que você queira dar uma olhada forum.nginx.org/read.php?2,13955,15981
chocripple
A solução no fórum é não alterar o URI da solicitação ao fazer o proxy para outro servidor, mas é exatamente isso que preciso fazer - reescreva o URI da solicitação para direcionar um aplicativo diferente do que o URL original inclui. Além disso, seu exemplo também parece usar o URI de solicitação no proxy_passcomando, portanto, não tenho certeza de como ele pode funcionar para você, dada a discussão no fórum acima.
Guss 15/05