Вопрос или проблема
У меня есть сервер nginx за балансировщиком нагрузки, сервер nginx передает запросы различным службам, но в данном случае – контейнеру docker, работающему на apache. Балансировщик нагрузки правильно устанавливает заголовок X-Forwarded-For, но когда он доходит до контейнера docker, X-Forwarded-For устанавливается на IP LB.
У меня есть следующее в конфигурации nginx:
/etc/nginx/conf.d/real_ip.conf
set_real_ip_from {{LB IP}};
real_ip_header X-Real-IP;
real_ip_recursive on;
А вот виртуальный хост:
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name *.domain domain;
include /etc/nginx/snippets/domain_ssl.conf;
add_header X-Nginx-Debug "hi";
proxy_pass_request_headers on;
location / {
proxy_pass_request_headers on;
proxy_pass http://container-php;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Remote-Addr $remote_addr;
proxy_set_header X-Real-IP $http_x_real_ip;
proxy_set_header X-Header-Test "Hello World - $http_x_forwarded_for";
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Но то, что я получаю от контейнера:
array(19) {
["Connection"]=>
string(7) "upgrade"
["Host"]=>
string(19) "domain"
["X-Forwarded-For"]=>
string(12) "{{LB IP}}"
["X-Header-Test"]=>
string(13) "Hello World -"
["X-Forwarded-Proto"]=>
string(5) "https"
["cache-control"]=>
string(9) "max-age=0"
["sec-ch-ua"]=>
string(64) "" Not;A Brand";v="99", "Google Chrome";v="97", "Chromium";v="97""
["sec-ch-ua-mobile"]=>
string(2) "?0"
["sec-ch-ua-platform"]=>
string(9) ""Windows""
["upgrade-insecure-requests"]=>
string(1) "1"
["user-agent"]=>
string(114) "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36"
["accept"]=>
string(135) "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9"
["sec-fetch-site"]=>
string(4) "none"
["sec-fetch-mode"]=>
string(8) "navigate"
["sec-fetch-user"]=>
string(2) "?1"
["sec-fetch-dest"]=>
string(8) "document"
["accept-encoding"]=>
string(17) "gzip, deflate, br"
["accept-language"]=>
string(26) "en-GB,en-US;q=0.9,en;q=0.8"
}
Замечательно, что X-Real-IP, X-Forwarded-For не кажутся установленными, как и remote_addr. Файлы, обслуживаемые напрямую из nginx, имеют правильно установленный x-forwarded-for, поэтому LB отправляет правильный заголовок.
Не упустил ли я что-то важное?
Я думаю, проблема в вашей конфигурации реального IP.
set_real_ip_from {{LB IP}};
real_ip_header X-Real-IP;
real_ip_recursive on;
Когда real_ip_header должен быть (в вашем случае) установлен на X-Forwarded-For. Я предположу, что ваш заголовок X-Forwarded-For от LB выглядит так:
X-Forwarded-For: {{Original client ip}}, {{LB ip}}
Таким образом, когда вы устанавливаете real_ip_header (заголовок, используемый для замены IP клиента) на X-Forwarded-For, он будет соответствовать оригинальному IP клиента. Оригинальный клиент теперь будет находится под переменной $realip_remote_addr, которую вы можете использовать для proxy_set_header X-Forwarded-For:
proxy_set_header X-Forwarded-For $realip_remote_addr
Пожалуйста, дайте знать, если я смог помочь!
Это утверждение переопределяет заголовок X-Forwarded-For:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Предполагая, что вы хотите сохранить оригинальный IP клиента в этом заголовке, вам следует написать:
proxy_set_header X-Forwarded-For $remote_addr;
Судя по вашему описанию, ваша проблема не связана напрямую с nginx, а с apache.
В зависимости от вашей конфигурации поток трафика может выглядеть следующим образом:
outside -> nginx -> apache -> php (работает как fpm)
или
outside -> nginx -> apache + модуль php
Вам следует ознакомиться с конфигурацией apache, чтобы убедиться, что apache не отбрасывает заголовки x-forwarded-for
и x-real-ip
при передаче запроса на PHP.
Если ваш поток трафика соответствует первому примеру, у вас apache проксирует запрос и на php, что может привести к большему количеству проблем, если nginx и apache не находятся в “синхронизации”.
Если бы вы обрабатывали проксирование php только с помощью nginx, вам нужно было бы добавить следующее в вашу конфигурацию location
для php:
location ~ \.php$ {
#...другие правила
fastcgi_param REMOTE_ADDR $http_x_real_ip;
#...другие правила
}
Таким образом, REMOTE_ADDR
PHP будет правильно установлен.
Что касается вашей конфигурации nginx, если ваш nginx {{LB IP}}
статический, вы можете установить его прямо в конфигурации.
/etc/nginx/conf.d/real_ip.conf
set_real_ip_from {{LB IP}};
real_ip_recursive on;
Вам не нужен real_ip_header X-Real-IP;
в вашем конфигурационном файле. Это переопределит директиву set_real_ip_from
.
Имейте в виду, что вам необходимо иметь включенный модуль real_ip в nginx, чтобы это работало.
В конечном итоге я решил это таким образом, который работает, но не решает корневую проблему. Насколько я могу судить, вышестоящий LB устанавливает “X-Forwarded-For” на удаленный адрес, и магия nginx, пытающаяся правильно установить это для обратного прокси, всегда ошибалась и устанавливала это на адрес LB или пустую строку.
Вместо этого я переключился с HTTP на протокол PROXY для связи LB->Сервер, установил следующее в /etc/nginx/proxy_params:
proxy_set_header X-Real-IP $proxy_protocol_addr;
proxy_set_header X-Forwarded-For $proxy_protocol_addr;
и это в conf.d:
/etc/nginx/conf.d/real_ip.conf
set_real_ip_from {{ LB_IP }};
real_ip_header proxy_protocol;
адаптировано от этого:
https://www.x33u.org/docs/kubernetes-stuff/hetzner-loadbalancer-setup/
и теперь все работает.
На случай, если кто-то столкнется с этой проблемой. Вот что я узнал после многих часов поиска.
- onlyoffice, работающий внутри docker, на сегодняшний день будет перенаправлять что угодно, что попадает на порт 80 (http), на порт 443 (https).
- onlyoffice, работающий внутри docker, перенаправит путь / на /welcome, и так же для docservice. Переменная, которую он использует, это $the_host, которая сопоставляется с $http_x_forwarded_host, и это $http_x_forwarded_host обозначает X-Forwarded-Host. Вы можете увидеть это сами в конфигурации под /etc/nginx/includes/xxx.conf.
map $http_x_forwarded_host $the_host {
default $http_x_forwarded_host;
"" $this_host;
}
- Таким образом, если у вас есть другой обратный прокси для onlyoffice. Вы должны передать правильное значение “X-Forwarded-Host” в nginx внутри контейнера.
- Например, в Nginx Proxy Manager вам нужно добавить пользовательское местоположение и добавить это в поле для дополнительного редактирования.
proxy_set_header X-Forwarded-Host $http_host;
- Еще один момент о $http_host: этот параметр включает запрашиваемый порт, если вы используете другой https порт, чем порт по умолчанию 443. Если вы используете Apache, у вас, вероятно, не будет никаких проблем с использованием конфигурации по умолчанию. Потому что Apache использует $http_host по умолчанию. Пока nginx использует $host по умолчанию, который не включает порт. Поэтому, если не объявлено явно, nginx не передаст порт на задний прокси или чему-либо в цепочке за ним.
Ответ или решение
Как заставить Nginx не перезаписывать заголовок X-Forwarded-For при проксировании
Введение
Когда Nginx используется в связке с балансировчиком нагрузки, часто возникает проблема с неправильной передачей IP-адресов клиента. Для корректного получения IP-адресов необходимо настроить Nginx так, чтобы он правильно обрабатывал заголовки X-Forwarded-For
и X-Real-IP
. В этой статье мы подробно рассмотрим, как настроить Nginx, чтобы он не перезаписывал X-Forwarded-For
, а также какие другие шаги могут потребоваться для правильной конфигурации.
Проблема
У вас имеется сервер Nginx, который проксирует запросы в контейнер Docker, работающий с Apache. Балансировщик нагрузки корректно устанавливает заголовок X-Forwarded-For
, однако при передаче этого запроса в контейнер, Nginx изменяет значение заголовка на IP-адрес балансировщика.
Шаги для решения проблемы
-
Настройка модуля real_ip
Убедитесь, что ваш конфигурационный файл для модуляreal_ip
правильно настроен, чтобы корректно обрабатывать заголовок. Вот базовые настройки, которые помогут вам:# /etc/nginx/conf.d/real_ip.conf set_real_ip_from {{LB_IP}}; # IP-адрес вашего балансировщика real_ip_header X-Forwarded-For; real_ip_recursive on;
Важно, чтобы
real_ip_header
был установлен наX-Forwarded-For
. Это позволит Nginx взять оригинальный IP-адрес клиента из этого заголовка. -
Настройка виртуального хоста
В конфигурации вашего виртуального хоста убедитесь, что заголовокX-Forwarded-For
не перезаписывается:server { listen 443 ssl; server_name *.domain.com; location / { proxy_pass http://container-php; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $http_x_forwarded_for; # Используйте заголовок из предыдущего запроса proxy_set_header X-Forwarded-Proto $scheme; } }
Здесь
proxy_set_header X-Forwarded-For $http_x_forwarded_for;
гарантирует, что оригинальный заголовок передается дальше без изменения. -
Проверка конфигурации Apache
Если после всех изменений проблема не решается, важно проверить конфигурацию Apache. Если он не настроен на обработку заголовковX-Forwarded-For
, это может привести к игнорированию этих данных.Для настройки Apache убедитесь, что вы используете соответствующие модули и директивы для передачи заголовков:
# Пример конфигурации в Apache <IfModule mod_remoteip.c> RemoteIPHeader X-Forwarded-For RemoteIPTrustedProxy {{LB_IP}} </IfModule>
-
Использование PROXY протокола
Если все вышеперечисленное не дает нужного результата, рассмотрите возможность использования PROXY протокола. Это позволит передавать информацию о клиентах более надежно, избегая проблем со скрытием реальных IP-адресов:# Включение PROXY протокола в конфигурации Nginx server { listen 443 ssl proxy_protocol; server_name *.domain.com; location / { proxy_pass http://container-php; proxy_set_header X-Real-IP $proxy_protocol_addr; proxy_set_header X-Forwarded-For $proxy_protocol_addr; } }
Заключение
Правильная настройка Nginx для передачи заголовков X-Forwarded-For
и X-Real-IP
очень важна для обеспечения безопасности и корректной работы приложения. Следуя этим рекомендациям, вы сможете избежать проблем с неверной передачей IP-адресов клиентов, что обеспечит лучшую работу вашего сервера и приложений.
Если после выполнения всех шагов у вас все еще возникают проблемы, стоит обратиться к логам Nginx и Apache для более детальной диагностики или рассмотреть конфигурацию других сетевых компонентов, таких как балансировщик нагрузки.