новая версия модуля ngx_http_realip_module

Maxim Dounin mdounin на mdounin.ru
Пн Июн 12 01:26:57 UTC 2023


Hello!

On Mon, Jun 12, 2023 at 02:31:04AM +0300, Gena Makhomed wrote:

> On 09.06.2023 9:29, Maxim Dounin wrote:
> 
> >> (1) client ==> vps_server ==> main_server
> >>
> >> (2) client ==> cloudflare => vps_server ==> main_server
> 
> > Если у вас в обоих случаях vps_server проксирует всё через stream
> > с proxy_protocol, то на принимающей стороне вам в любом случае
> > надо сначала достать реальный адрес клиента из proxy_protocol.
> > А уже потом смотреть в заголовки (или не смотреть, если на
> > vps_server пришли не с IP-адресов Cloudflare).
> > 
> > То есть, фактически, для корректной работы такой схемы - нужен
> > "real_ip_recursive on;" (http://nginx.org/r/real_ip_recursive)
> > и заголовок со списком нужных адресов.
> > 
> > Сейчас из коробки такое можно сделать дополнительным
> > проксированием с установкой заголовка.  Для стандартного заголовка
> > X-Forwarded-For, благо Cloudflare его ставит, конфигурация будет
> > выглядеть как-то так:
> > 
> > server {
> >      listen 8080 proxy_protocol;
> >     
> >      set_real_ip_from <vps>;
> >      real_ip_header proxy_protocol;
> > 
> >      location / {
> >          proxy_pass http://127.0.0.1:8081;
> >          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
> >      }
> > }
> > 
> > server {
> >      listen 8081;
> >      
> >      set_real_ip_from 127.0.0.1;
> >      set_real_ip_from <cloudflare>;
> > 
> >      real_ip_header X-Forwarded-For;
> >      real_ip_recursive on;
> > 
> >      ...
> > }
> 
> Это будет работать только в том случае, если система защиты от DDoS
> указывает IP адрес клиента в заголовке X-Forwarded-For. Если же какая-то
> экзотическая система защиты от DDoS будет указывать реальный IP клиента
> в каком-то другом заголовке, например, только в загловке X-Real-IP -
> тогда этот метод работать не будет, потому что в nginx есть переменная
> $proxy_add_x_forwarded_for но в nginx нет перменной $proxy_add_x_real_ip
> 
> Что же нам делать в том случае, если какая-то система защиты от DDoS
> передает реальный IP клиента в заголовке отличном от X-Forwarded-For ?

Если нужно работать с нестандартными заголовками - это можно 
сделать с помощью модуля map, благо сейчас его возможностей с 
лихвой хватает для реализации логики, аналогичной работе 
переменной $proxy_add_x_forwarded_for.

Не говоря уже о том, что, по большому счёту, в данной конкретной 
задаче полная логика $proxy_add_x_forwarded_for не важна, и вполне 
можно обойтись безусловным добавлением адреса через запятую:

    proxy_set_header X-Real-IP "$http_x_real_ip, $remote_addr";

В вырожденном случае пустого $http_x_real_ip будет лишняя запятая 
в начале, но на работоспособность это не влияет.

> >> Или существует какой-то еще лучший вариант решения этой задачи?
> > 
> > Я периодически думаю о том, чтобы научить модуль realip брать
> > список IP-адресов не из заголовка, а непосредственно из
> > переменной.  Тогда необходимость в дополнительном проксировании в
> > подобных странных конфигурациях отпадёт.
> 
> Каким же образом тут можно будет обойтись без двойного проксирования,
> если необходимо будет сначала указывать real_ip_header proxy_protocol;
> чтобы узнать реальный IP сервера cloudflare, который подключился к VPS,
> и при этом - несколько директив real_ip_header нельзя указывать в одном
> контексте. А если указать одну директиву real_ip_header одновременно
> и в контексте server и одну в контексте location, то согласно правил
> наследования директив в nginx - директива real_ip_header в контексте
> location будет выполняться, а директива real_ip_header в контексте
> server выполняться не будет, и всегда будет просто проигнорирована.
> 
> Максим, можете пояснить в чем тут моя ошибка и что я не так понял?

Идея состоит в том, что вместо указания названия заголовка в 
директиве real_ip_header - указать непосредственно адреса.  Тогда 
вместо

    real_ip_header proxy_protocol;

можно будет написать что-то вроде:

    real_ip_address $proxy_protocol_addr;

А для последующего рекурсивного поиска в заголовке X-Forwarded-For 
(если он получен с доверенного адреса), соответственно, будет 
достаточно написать:

    real_ip_address "$http_x_forwarded_for, $proxy_protocol_addr";

И, соответственно, полная конфигурация будет выглядеть как:

server {
    listen 8080 proxy_protocol;
     
    set_real_ip_from <vps>;
    set_real_ip_from <cloudflare>;

    real_ip_address "$http_x_forwarded_for, $proxy_protocol_addr";
    real_ip_recursive on;

    ...
}

То есть ровно то, что в примере выше делается с помощью 
дополнительного проксирования - в такой схеме будет сделано с 
помощью установки переменной.

[...]

> Максим, вопрос в том, можно ли будет в основной ветке nginx заменить
> текущую версию модуля ngx_http_realip_module новой версией модуля,
> в которой будет реализован вариант директивы set_real_ip_from
> с одним, тремя и пятью параметрами?

Я не вижу решительно никаких причин переусложнять работу модуля 
подобным образом.  Имеющиеся задачи вполне решаются с помощью 
существующих возможностей модуля, что и было продемонстрировано в 
исходном ответе.  С помощью минимальных (в первую очередь с точки 
зрения пользователя) доработок можно сделать так, чтобы подобные 
задачи решались эффективнее - однако, как уже было сказано, острой 
необходимости в этом пока не прослеживается.

[...]

-- 
Maxim Dounin
http://mdounin.ru/


Подробная информация о списке рассылки nginx-ru