Браузер запрашивает https://203.0.113.10:8443, прокси принимает соединение, а служба на пире WireGuard так и не отвечает. Правило DNAT делает свою работу; сбой происходит в ответе. Проследите пакет через оба преобразования, и недостающее правило станет очевидным. Исправление — одна дополнительная инструкция NAT в нужном хуке, а не новый туннель.

Проследите один запрос от начала до конца

Запрос приходит на публичный интерфейс прокси, источник — публичный адрес клиента, назначение — адрес прокси. В nat PREROUTING правило DNAT перезаписывает назначение на адрес туннеля пира, 10.90.0.2. Затем решение маршрутизации отправляет пакет наружу через wg0, и путь пересылки завершён.

На пире пакет теперь несёт исходный публичный адрес источника, потому что его ничто не перезаписывало. Служба принимает соединение и отправляет ответ на свой шлюз по умолчанию — домашний маршрутизатор перед пиром. У этого маршрутизатора нет состояния для потока, который он никогда не видел, и он либо отбрасывает пакет, либо транслирует его на свой публичный адрес, чего клиент не примет.

Асимметрия и есть вся проблема: запрос идёт через прокси, а ответ пытается идти напрямую. Отслеживание соединений прокси помогает, только если ответ возвращается через прокси, а вернуться туда он не может, пока адрес источника указывает на исходного клиента.

Добавьте обратное преобразование

Перезапишите источник пересылаемого потока на адрес туннеля прокси, прежде чем он уйдёт к пиру. Тогда служба отвечает на 10.90.0.1, прокси получает ответ, и отслеживание соединений обращает оба преобразования, прежде чем пакет вернётся к клиенту.

iptables -t nat -A PREROUTING -p tcp -m multiport --dports 8080,8443 -j DNAT --to-destination 10.90.0.2
iptables -t nat -A POSTROUTING -d 10.90.0.2 -o wg0 -p tcp -m multiport --dports 8080,8443 -j SNAT --to-source 10.90.0.1

Правило POSTROUTING намеренно узкое: оно совпадает с транслированным назначением и интерфейсом туннеля, поэтому не перезаписывает посторонний трафик, случайно выходящий через wg0. Широкое правило masquerade тоже скрыло бы адрес клиента, но сделало бы это для каждого потока и усложнило бы аудит таблицы NAT.

Разместите правила там, где существует туннель

Правила, ссылающиеся на адрес или интерфейс туннеля, должны выполняться после того, как wg-quick его настроит. Интерфейс создаётся до PreUp, но адреса, MTU, DNS и маршруты применяются после, а PostUp запускается, когда всё это на месте. Дополнения поместите в PostUp, а удаления — в PreDown.

PostUp = iptables -t nat -A PREROUTING -p tcp -m multiport --dports 8080,8443 -j DNAT --to-destination 10.90.0.2
PostUp = iptables -t nat -A POSTROUTING -d 10.90.0.2 -o wg0 -p tcp -m multiport --dports 8080,8443 -j SNAT --to-source 10.90.0.1
PreDown = iptables -t nat -D PREROUTING -p tcp -m multiport --dports 8080,8443 -j DNAT --to-destination 10.90.0.2
PreDown = iptables -t nat -D POSTROUTING -d 10.90.0.2 -o wg0 -p tcp -m multiport --dports 8080,8443 -j SNAT --to-source 10.90.0.1

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

Проверьте результат с помощью iptables -t nat -S после переподключения. Если то же правило появляется дважды, строка PreDown не совпадает, и очистка молча не работает.

Пересылка, фильтрация и другой NAT

Включите пересылку IPv4 постоянно, затем разрешите пересылаемый поток в таблице фильтра. DNAT происходит до цепочки FORWARD, поэтому правило фильтра совпадает с транслированным назначением, а не с публичным адресом. Если UFW управляет брандмауэром, выразите разрешение как правило маршрута; обычное правило хоста не покрывает пересылаемый трафик.

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

На VPS с NAT провайдер сопоставляет публичный порт с частным адресом. Сопоставление должно совпадать с объявляемым портом, а несовпадение выглядит так же, как сломанный туннель. Подтвердите сопоставление у провайдера, прежде чем менять конфигурацию WireGuard.

Проверьте обратный путь и примите цену

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

conntrack -L -p tcp --dport 8080
tcpdump -ni any tcp port 8080

Запись conntrack должна показывать исходный кортеж, транслированный кортеж и направление ответа. Если ответ отсутствует, пир не отправляет через туннель, что обычно означает, что правило SNAT не применилось или собственный брандмауэр пира отбросил поток.

Перезагрузитесь один раз, чтобы доказать, что PostUp снова применяет правила, затем остановите туннель и подтвердите, что PreDown их удалил. Мёртвые правила, переживающие отключение, — причина того, что последующее изменение ведёт себя непредсказуемо.

Наконец, примите цену SNAT. Служба видит каждый запрос как пришедший от прокси, поэтому журналы доступа, ограничения скорости на клиента и списки разрешённых адресов больше не различают пользователей. Случаю hairpin нужно собственное преобразование источника для клиентов в LAN прокси. Если пиру нужны настоящие адреса клиентов, альтернатива — обратный маршрут к сети клиента, это больше работы, но сохраняет информацию.

Похожие статьи