nginx auth_request (1/3): Вводная

У меня тут накопилось немного опыта работы с этим модулем, решил поделиться.

Прежде всего - что это? Это модуль, который разрешает или запрещает прохождение запроса в nginx на основе подзапроса. Две основных схемы применения:

  • с его помощью можно соорудить WAF (web-application firewall)
  • ...и кастомный портал предварительной авторизации

...всё перечисленное - без модификации исходного сайта.

Выглядит это примерно так. Вот у нас есть типовой запрос, приходящий на некоторый вебсервер:

GET /files/83084_s.jpg HTTP/1.1
Host: example.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:52.0) Gecko/20100101 Firefox/52.0
Accept: */*
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive

nginx пересылает его сначала на указанный location, затем на основе ответа, решает что делать - пустить дальше или выдать ошибку.

Общая схема запросов (исходник):

Authorizer - это отдельная internal локация nginx'а. Там может быть или proxy_pass на внешний сервер, или вызов скриптов с этого же сервера.

Примерный конфиг nginx'а:

location / {
  auth_request /auth;
  proxy_pass http://site.example.com;
}
location = /auth {
  internal;
  proxy_pass http://127.0.0.1/check.pl;
  proxy_pass_request_body off;         # <- важно
  proxy_set_header Content-Length "0"; # <- важно
  proxy_set_header X-Original-URI $request_uri;
}

Обратите внимание на Content-Length "0". Нужно это затем, чтобы в пересылаемом POST запросе получатель не ждал данных.

Далее, допустим мы соорудили некий check.pl, который на основе пересланных запросов будет отвечать кодами 200/401/403/etc. С 200/OK - всё ясно, запрос проходит дальше. В случае остальных, например 403 - в дефолте nginx покажет простенькую, и совершенно неинформативную страничку "Access Denied". Чтобы этого не было, нам нужно добавить в блок location / {} перехват этих кодов:

location / {
  <...>
  error_page 401 /auth.pl;
  error_page 403 /auth.pl;
}

... где /auth.pl -- страница авторизации или сообщения об ошибке.

Здесь вырисовывается две проблемы: во-первых, нам нужно прописать локейшн и для /auth.pl, во-вторых -- 401/403 коды могут использоваться и в самом сайте. Первое бы хрен с ним, но второе -- реально проблема, если перехватывать все 403 с сайта, мы можем использовать для этой ошибки только одну глобальную страницу на сайт.

Для обхода этого кейса, я написал небольшой патч, который позволяет также использовать код 302, временный редирект. Правда с включением в апстрим меня завернули, дескать это поломает использование этого модуля как одного из факторов авторизации:

location / { 
  proxy_pass http://site.example.com;
  auth_req /auth;         # запрос должен быть авторизован через nginx authreq
  allow 192.168.0.0/16;   # ...ИЛИ идти из локальной сети
  deny all;
  satisfy any;            # <- "или" - берётся отсюда
}

В принципе, надо - берите. Если таки забодаете апстрим - вообще респект и уважуха :-).

Далее покажу примеры реального использования для каждого случая.