Как мне сделать так, чтобы nginx не перезаписывал x-forwarded-for при проксировании?

Вопрос или проблема

У меня есть сервер 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/

и теперь все работает.

На случай, если кто-то столкнется с этой проблемой. Вот что я узнал после многих часов поиска.

  1. onlyoffice, работающий внутри docker, на сегодняшний день будет перенаправлять что угодно, что попадает на порт 80 (http), на порт 443 (https).
  2. 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;
}
  1. Таким образом, если у вас есть другой обратный прокси для onlyoffice. Вы должны передать правильное значение “X-Forwarded-Host” в nginx внутри контейнера.
  2. Например, в Nginx Proxy Manager вам нужно добавить пользовательское местоположение и добавить это в поле для дополнительного редактирования.
proxy_set_header X-Forwarded-Host $http_host;
  1. Еще один момент о $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-адрес балансировщика.

Шаги для решения проблемы

  1. Настройка модуля 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-адрес клиента из этого заголовка.

  2. Настройка виртуального хоста
    В конфигурации вашего виртуального хоста убедитесь, что заголовок 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; гарантирует, что оригинальный заголовок передается дальше без изменения.

  3. Проверка конфигурации Apache
    Если после всех изменений проблема не решается, важно проверить конфигурацию Apache. Если он не настроен на обработку заголовков X-Forwarded-For, это может привести к игнорированию этих данных.

    Для настройки Apache убедитесь, что вы используете соответствующие модули и директивы для передачи заголовков:

    # Пример конфигурации в Apache
    <IfModule mod_remoteip.c>
       RemoteIPHeader X-Forwarded-For
       RemoteIPTrustedProxy {{LB_IP}}
    </IfModule>
  4. Использование 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 для более детальной диагностики или рассмотреть конфигурацию других сетевых компонентов, таких как балансировщик нагрузки.

Оцените материал
Добавить комментарий

Капча загружается...