Контролировать цель прокси Nginx с помощью куки?

11

Я пытаюсь преобразовать обратный прокси-сервер, используя интересную настройку Apache mod_rewrite, чтобы использовать вместо него Nginx (из-за внешних проблем мы переходим с Apache на Nginx, и почти все работает отлично, кроме этой части).

Моя первоначальная установка состояла в том, чтобы прочитать HTTP-cookie (установленный некоторым приложением) и, в зависимости от его значения, направить обратный прокси-сервер в разные бэкэнды. Это пошло примерно так:

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]

Я пытаюсь добиться того же, используя Nginx, и моя первоначальная конфигурация была примерно такой (где proxy_override - это имя файла 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;
}

Но это не так. Я попытался выяснить, может ли Nginx прочитать мой файл cookie, написав основной прокси-сервер для перенаправления на что-либо на основе, ${cookie_proxy_override}и я вижу, что он хорошо читает содержимое, но ifкажется, что всегда происходит сбой.

Моя следующая попытка, согласно ответу Рикиха, была такой:

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

И теперь я вижу, что ifблок активируется, но вместо того, чтобы проксировать запрос (как я и думал), он возвращает перенаправление 302 на указанный URL-адрес - это не то, что я пытаюсь сделать: мне нужен сервер прозрачно передать запрос бэкэндам и передать ответ исходному клиенту.

Что я делаю неправильно?

Guss
источник

Ответы:

16

Похоже на этот ответ . Идиоматический подход Nginx к такого рода проблемам - через map.

По сути, вы определяете mapв httpразделе

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

Тогда вы просто используете $my_upstreamв locationразделе (ах):

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

Nginx оценивает переменные карты лениво, только один раз (для каждого запроса) и при их использовании.

Александр Азаров
источник
3
Спасибо, это лучший подход, чем мой, особенно потому, что я могу использовать именованную переменную cookie напрямую (не знаю, почему я не могу войти if), и я ее реализовал. Однако есть одна проблема - Nginx (по крайней мере, моя версия: 1.0.0) не любит пронумерованные записи map, поэтому мне пришлось использовать ~^(?P<name>[\w-]+) $name;вместо этого. Я отредактировал ваш ответ соответственно.
Гусс
3

Со временем мое решение сводится к следующему:

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

Тест проводится в области serverдействия для каждого запроса (до разрешения фактического перенаправления) и просто используется для установки переменной - это, очевидно, поддерживаемое использование модуля перезаписи Nginx. Он также проверяет все $http_cookieкак предложено @Rikih, но включает в себя имя куки, чтобы убедиться, что я не сопоставляю случайные вещи, которые люди могут бросать на меня.

Затем в locationобласти, где я хочу выполнить перенаправление, я использую имя переменной, которая либо содержит исходную конфигурацию по умолчанию, либо была перезаписана файлом cookie.

Guss
источник
0

Вы пробовали $ http_cookie? http://wiki.nginx.org/HttpRewriteModule

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

chocripple
источник
Это действительно сработало для теста, хотя я не уверен, почему я не могу просто проверить конкретное имя куки. Что мне не понравилось, так это то, rewriteчто на самом деле не происходит перезапись прокси, а вместо этого возвращает перенаправление клиенту, и я не могу использовать proxy_pass в ifблоке. Я обновил вопрос соответственно.
Гусс
0

У меня есть пример, который я использую для определения заголовка запроса на основе UDID, и он работает, может быть, вы получите некоторое представление.

   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
источник
Какую версию Nginx вы используете? Я использую 1.0 и когда я использую proxy_pass, как вы указали здесь, я получаю это сообщение об ошибке: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
я использую nginx-0.8.53-1.el5
chocripple
может быть, вы хотите взглянуть forum.nginx.org/read.php?2,13955,15981
chocripple
Решение на форуме состоит в том, чтобы не изменять URI запроса при прокси на другом сервере, но это именно то, что мне нужно сделать - переписать URI запроса для таргетинга приложения, отличного от того, что включает в себя исходный URL. Кроме того, ваш пример, похоже, также использует URI запроса в proxy_passкоманде, поэтому я не уверен, как он может работать для вас, учитывая вышеупомянутое обсуждение на форуме.
Гусс