Три хопа WireGuard достигают сети без публичного адреса, и затем средняя машина перестаёт отвечать на порту, на котором должна была слушать. Туннель дальше по-прежнему работает, удалённый хост по-прежнему отвечает, и только сам средний сервер замолчал снаружи. Это сочетание сбивает с толку именно потому, что создано им же: средний хоп подчиняется собственной таблице маршрутизации, а таблица отправляет его ответы не в ту сторону.

Цепочка, которая работает, пока вы не заговорите со средней машиной

Возьмём иллюстративную цепочку: клиент A достигает сервера B по UDP 500, а B идёт дальше к серверу C по UDP 500. C принимает трафик только с адреса своего единственного партнёра, что делает его листом и сохраняет поведение простым. B не лист, и всё интересное в этой неисправности происходит там.

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

Почему ответ уходит через туннель

На B интерфейс, обращённый к C, несёт AllowedIPs = 0.0.0.0/0 вместе с соответствующим маршрутом по умолчанию. Поэтому каждый пакет, созданный B, попадает в этот туннель, если только что-то более конкретное не получит приоритет.

Пакет от A, адресованный B, приходит и принимается, потому что B — это пункт назначения, а локальная доставка происходит первой. Ответ — совершенно новый локально созданный пакет, направленный на публичный адрес A, и это назначение совпадает с маршрутом по умолчанию в туннель. C получает его, не владеет этим адресом и отбрасывает его.

Симптом читается как проблема межсетевого экрана или несовпадение ключей, и здесь неверно ни то, ни другое. Захват на туннеле в сторону C показывает, что ответы идут не в ту сторону. Это одно наблюдение экономит больше времени, чем любое количество чтения конфигурации, поэтому запишите его, прежде чем начнёте менять ключи. Любой, кто не видел захвата, будет считать, что сломан сам туннель.

Узкое исправление: правила политики, совпадающие с портом источника

Ответы из слушающего сокета B уходят с портом источника 500. Правило политики может совпасть с этим и вернуть эти пакеты в обычную таблицу:

ip rule add sport 500 table main priority 100
ip rule add sport 8080 table main priority 101

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

Знайте границу этого приёма. Селектор sport совпадает с локально созданными пакетами, что покрывает ровно случай ответа и намеренно не покрывает трафик, который B только перенаправляет за кого-то другого. Служба, размещённая за B, по-прежнему нуждается в собственном ответе, а утверждение обратного даёт исправление, работающее для первой службы и терпящее неудачу для второй.

Дайте правилу владельца

Правило, набранное в командной строке, исчезает при следующей перестройке интерфейса. В настройке WireGuard поместите его туда, где интерфейс создаётся и удаляется:

PostUp = ip rule add sport 500 table main priority 100
PostDown = ip rule del sport 500 table main priority 100

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

Сравните альтернативы честно

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

ApproachWhat it costsWhere it breaks
Source-port policy ruleOne rule per exposed portAnything B forwards rather than originates
Destination exception routeA route pinned to specific client addressesClients whose address changes
Stop full-tunnelling on BLoses the catch-all path toward CDesigns that depend on it
Split into two tunnelsMore interfaces and more policyNothing, at the cost of complexity

Проверьте с обеих сторон после выбора. Со стороны A начните соединение к порту B и подержите его занятым некоторое время. На B заставьте ядро обосновать путь ответа чем-то вроде ip route get <address-of-A> sport 500 и убедитесь, что оно называет интерфейс WAN, а не туннель. После этого проверьте, что что-то за C по-прежнему отвечает через цепочку, что доказывает: исключение осталось узким, а не превратилось в обход. Наконец, запишите порты в проектных заметках, иначе следующий оператор обнаружит это заново на другой службе.

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