iptables – в nginx “proxy_bind transparent” не работает (или как использовать “proxy_protocol”)

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

Я использую nginx для мультиплексирования SSH и HTTPS с помощью этого файла /etc/nginx/nginx.config:

stream {
    upstream ssh {
        server 127.0.0.1:22;
    }

    upstream https {
        server 127.0.0.1:8443;
    }
    
    map $ssl_preread_protocol $upstream {
        default ssh;
        "TLSv1.2" https;
        "TLSv1.3" https;
        "TLSv1.1" https;
        "TLSv1.0" https;
    }

    server {
        listen 443;
        proxy_pass $upstream;
        ssl_preread on;
    }
}

Это успешно направляет трафик, но имеет побочный эффект: все соединения выглядят как приходящие с 127.0.0.1 как для SSH, так и для HTTPS:

$ last
jason    pts/3        127.0.0.1        Ср Фев 19 22:25 - 22:25  (00:00)
...
$ tail -1 /var/log/nginx/access.log
127.0.0.1 - - [19/Фев/2025:20:31:36 +0100] "GET /favicon.ico HTTP/1.1" 200 1406 "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36"

Я пытался использовать прозрачное проксирование, изменив файл /etc/nginx/nginx.config следующим образом:

user root; # было: www-data
...
   server {
      listen 443;
      proxy_bind $remote_addr transparent;
      proxy_pass $upstream;
      ssl_preread on;
   }
...

Однако это вызывает зависание и тайминги входящих соединений SSH и HTTPS. Я пытался отключить файрвол ufw, но это не дало эффекта.

Документация proxy_bind недостаточно определенна по поводу того, что делать: https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_bind

Для работы этого параметра обычно необходимо запускать рабочие процессы nginx с привилегиями суперпользователя. На Linux это не требуется (1.13.8), так как если указан параметр transparent, рабочие процессы наследуют способность CAP_NET_RAW от главного процесса. Также необходимо настроить таблицу маршрутизации ядра для перехвата сетевого трафика с проксируемого сервера.

Я изменил на user root; и запускаю nginx через sudo systemctl start nginx, так что у меня должна быть часть “суперпользователя”, верно?

Как мне “настроить таблицу маршрутизации ядра для перехвата сетевого трафика с проксируемого сервера”? Обратите внимание, что я в настоящее время использую ufw для настройки iptables, если это имеет значение.


ИЗМЕНИТЬ: Альтернативно, если я смогу каким-то образом применить proxy_protocol или внедрить HTTP заголовки с $remote_addr для HTTP стороны (и сохранить SSH поток нетронутым), это уже решит половину проблемы: логи моего сайта будут содержать правильный IP адрес. Я, вероятно, смогу смириться с тем, что логи SSH неверны.

Это стандартный способ добавления удаленного IP адреса при проксировании:

proxy_set_header X-Real-IP $remote_addr;

Затем используйте заголовок X-Real-IP на вашем upstream веб-сервере для логирования IP адресов.

Ответ или решение

Теория и Контекст

В случае, если вы используете Nginx для мультиплексирования SSH и HTTPS трафика, ваши текущие настройки могут вызвать проблему, при которой все входящие соединения на серверы SSH и HTTPS отображаются как происходящие с IP-адреса 127.0.0.1. Это может затруднить ведение журналов и мониторинг реальных IP-адресов клиентов. Для того чтобы решить эту проблему, существует два подхода: использовать транспарентное проксирование или применять Proxy Protocol.

Транспарентное Проксирование

Транспарентное проксирование позволяет вам сохранять исходный IP-адрес клиента, тем самым сохраняя возможность отслеживать реальные адреса в логах сервера. Тем не менее, реализация требует дополнительных настроек сетевых правил. Попробуем разобраться, как это можно сделать.

  1. CAP_NET_ADMIN и CAP_NET_RAW: Убедитесь, что nginx работает с нужными привилегиями. В некоторых системах, даже если Nginx запущен от имени суперпользователя, для работоспособности транспарентного проксирования требуется, чтобы процессы nginx унаследовали возможности CAP_NET_RAW и CAP_NET_ADMIN. Дать такие права можно с помощью:

    sudo setcap 'cap_net_bind_service=+ep' /path/to/nginx
    sudo setcap 'cap_net_admin,cap_net_raw=+ep' /path/to/nginx
  2. Настройка таблиц маршрутизации: Необходимо настроить правила iptables для перехвата входящего трафика и его корректной маршрутизации. Попробуйте следующие команды:

    # Создаем правила для модуля nf_conntrack
    sudo iptables -t mangle -N DIVERT
    sudo iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
    sudo iptables -t mangle -A DIVERT -j MARK --set-mark 1
    sudo iptables -t mangle -A DIVERT -j ACCEPT
    sudo ip rule add fwmark 1 lookup 100
    sudo ip route add local 0.0.0.0/0 dev lo table 100
  3. Отладка: Убедитесь, что команда ip rule show подтверждает наличие правил маршрутизации с установленным fwmark. Также используйте команды iptables -t mangle -L и ip route show table 100 для проверки корректности настроек.

Если после выполнения всех шагов транспарентное проксирование не работает, вы можете столкнуться с специфическими ограничениями дистрибутива Linux или конфигурацией сети.

Proxy Protocol

Альтернативный путь к решению вашей проблемы — использование Proxy Protocol, который позволяет проксирующему серверу передавать информацию о реальном клиентском IP-адресе через дополнительный заголовок.

  1. Настройка Nginx: Добавьте в конфигурацию Nginx использование Proxy Protocol:

    server {
       listen 443 proxy_protocol;
       proxy_pass $upstream;
       ssl_preread on;
       # Другие настройки...
    }
  2. Настройка Upstream серверов: Убедитесь, что бэкенды HTTP поддерживают Proxy Protocol. Если это веб-сервер типа Apache или другой сервер, который не поддерживает Proxy Protocol напрямую, можно применить директивы вроде proxy_set_header и подставить IP-адрес клиента в X-Real-IP:

    proxy_set_header X-Real-IP $proxy_protocol_addr;
  3. Конфигурация SSH demon (sshd): Для SSH потребуется дополнительная обработка, поскольку многие версии sshd не поддерживают Proxy Protocol. В этом случае единственное возможное решение — разделить потоки таким образом, чтобы Proxy Protocol воздействовал только на HTTPS-трафик, а для SSH оставался прежний механизм.

Применение и Заключение

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

Рекомендую начать с настройкой Proxy Protocol, так как она более гибкая и реализуется через конфигурацию, что позволяет в дальнейшем легче отладить процесс. Как альтернативы можно рассмотреть использование протоколов и настроек другого уровня — например, сетевого, в случае отсутствия необходимых поддерживаемых версий приложений.

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

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