Цепочка клиента, доверенного балансировщика, Nginx и приложения с прокси-заголовками
DevOps

Исходный IP и прокси-заголовки в Nginx

Настраиваем X-Forwarded-For, X-Forwarded-Proto и real_ip в Nginx: сохраняем адрес клиента и не доверяем поддельным заголовкам.

Содержание

В журнале Nginx вместо адреса посетителя может появиться IP балансировщика или CDN. Исправлять это простым чтением X-Forwarded-For опасно: любой прямой клиент способен прислать такой заголовок со своим значением.

Сначала нужно обозначить границу доверия — точные адреса промежуточных узлов, которым разрешено сообщать исходный IP. Если посетитель подключается прямо к Nginx, адрес уже находится в $remote_addr. Если соединение приходит от известного proxy, модуль realip может заменить его данными из согласованного заголовка.

Нарисуйте цепочку запроса

Запишите путь запроса от посетителя до приложения. Следующая схема — пример с двумя промежуточными узлами; удалите из неё те, которых нет в вашей инфраструктуре:

клиент → CDN → внешний балансировщик → Nginx → приложение

На каждом шаге ответьте: кто установил TCP-соединение, кто может записать заголовок и какой адрес должен попасть в журнал. Без этой схемы легко принять подделку за реальный IP и сломать ограничения, аудит или формирование HTTPS-ссылок.

Nginx как первый публичный узел

Когда посетитель подключается непосредственно к Nginx, базовая прокси-конфигурация может выглядеть так:

location / {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

$host безопаснее прямого отражения произвольного $http_host, но допустимые host всё равно ограничиваются server_name. $proxy_add_x_forwarded_for добавляет текущий $remote_addr к существующей цепочке. Приложение не должно брать первый попавшийся адрес без настройки trusted proxies.

Nginx за доверенным proxy

Допустим, внешний балансировщик с адресом 192.0.2.10 очищает входной заголовок и записывает IP клиента в X-Forwarded-For. Пример документационного адреса:

set_real_ip_from 192.0.2.10/32;
real_ip_header X-Forwarded-For;
real_ip_recursive on;

В production подставьте опубликованные и проверенные сети своего поставщика. Не используйте 0.0.0.0/0: тогда любой прямой клиент сможет подменить адрес. Ограничьте прямой доступ к origin сетевым firewall, иначе злоумышленник обойдёт CDN и заголовочную модель.

После обработки $remote_addr станет вычисленным клиентским адресом, а $realip_remote_addr сохранит адрес узла, который физически подключился к Nginx. Полезно логировать оба во время проверки.

real_ip_recursive выбирают по реальной структуре цепочки. При нескольких proxy он позволяет искать последний недоверенный адрес, но только если все доверенные переходы перечислены корректно. Включение опции без карты сети не повышает безопасность.

Исходный протокол

Если TLS завершается на внешнем балансировщике, внутренний $scheme может быть http. Внешний узел должен установить X-Forwarded-Proto, а внутренний Nginx и приложение — принимать его только от доверенного источника. Иначе возможны циклы HTTPS-редиректа и cookies без флага Secure.

Не решайте это глобальным доверием ко всем forwarded-заголовкам. Лучше разделить listener, сеть или server-блок, доступный только балансировщику.

Проверка без самообмана

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

log_format proxycheck '$remote_addr peer=$realip_remote_addr '
                      'xff="$http_x_forwarded_for" host="$host" scheme="$scheme"';

Подключите формат к тестовому виртуальному хосту, выполните nginx -t, reload и сделайте запросы через обычный маршрут. Затем из разрешённой тестовой точки отправьте к origin заведомо поддельное значение:

curl -H 'X-Forwarded-For: 198.51.100.99' https://example.com/

Если прямой клиент меняет итоговый $remote_addr, граница доверия настроена неверно. Не публикуйте диагностический endpoint с полными заголовками: они могут содержать чувствительные данные.

Типовые ошибки

  • доверие всем адресам ради «правильных логов»;
  • устаревший список сетей CDN;
  • origin доступен в обход CDN;
  • приложение доверяет forwarded-заголовкам от любого peer;
  • берётся левый или правый элемент цепочки без учёта порядка proxy;
  • реальный адрес используется как единственный фактор авторизации.

IP-адрес подходит для журналирования и вспомогательных ограничений, но NAT, мобильные сети и proxy делают его плохим идентификатором пользователя.

Откат и дальнейшая проверка

Сохраните прежний конфиг. При ошибке верните set_real_ip_from и real_ip_header, проверьте nginx -t и reload. После изменения сопоставьте журналы Nginx, балансировщика и приложения. Основы проксирования разобраны в reverse proxy для приложения, а системная проверка — в логах Nginx.

Поддерживайте модель доверия

Сети облачного proxy способны изменяться. Назначьте источник обновлений, владельца и тест после замены списка, но не загружайте CIDR прямо в production без проверки формата и review. Удалённый диапазон может оставить origin открытым, а ошибочно добавленный — дать чужому узлу право подменять адреса.

Для нескольких окружений храните схему отдельно: production, staging и локальная разработка обычно имеют разные trusted proxies. Не включайте режим «доверять proxy» глобально в приложении только потому, что локальный Docker добавляет один переход.

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

Проверьте сценарии прямого обхода, обычного запроса через CDN, IPv4 и IPv6, а также поведение WebSocket, если он используется. В аудит записывайте не только вычисленный клиентский адрес, но и непосредственного peer, чтобы позже можно было восстановить путь запроса.

Первоисточники

Рекламное местоВаша компания здесьРазместить рекламу

Самопроверка

Проверьте, что материал усвоен

Ответьте на все вопросы. Результат сохранится только в этом браузере и будет учтён в статистике прочитанных материалов.

01Какой адрес допустимо указать в set_real_ip_from?
02Что делает $proxy_add_x_forwarded_for?
03Где ещё задаётся доверие к proxy кроме Nginx?

Разбираем коротко

Частые вопросы

Можно ли доверять X-Forwarded-For, который уже пришёл от клиента?

Нет. Клиент может подставить произвольное значение. Доверять можно только заголовку, добавленному известным proxy, а граница доверия должна быть закреплена адресами set_real_ip_from и сетевыми правилами.

Чем X-Real-IP отличается от X-Forwarded-For?

X-Real-IP обычно содержит один адрес, который Nginx считает клиентским, а X-Forwarded-For хранит цепочку адресов через proxy. Оба заголовка являются договорённостью и безопасны только при контролируемом формировании.

Почему приложение строит ссылки с http за HTTPS-балансировщиком?

Внутреннее соединение до приложения может быть HTTP, а исходный протокол не передан или не признан доверенным. Настройте X-Forwarded-Proto на proxy и список доверенных proxy в приложении.