Туннель как будто работает только после того, как сервер первым что-то отправит. Запустите с клиента — ничего не происходит, подождите несколько секунд, попробуйте снова — и получается. Обычный диагноз — сломанный NAT, и обычный диагноз неверен. Изменилось то, что одна сторона узнала, где находится другая.

Знание конечной точки, а не достижимость

Сторона WireGuard не начнёт рукопожатие, пока не узнает, куда его отправлять. Конфигурация задаёт это как конечную точку из адреса и порта, а рабочее состояние отслеживает конечную точку, в которую оно верит сейчас.

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

Как сторона узнаёт конечную точку

WireGuard намеренно молчалив. Когда корректно аутентифицированный пакет приходит с адреса, которого сторона не ожидала, реализация обновляет конечную точку этой стороны на новый источник, и именно так работает роуминг на ноутбуке, который меняет сети. Такое поведение объясняет зависимость от направления: если сервер отправляет первым и его пакет доходит до клиента, клиент узнаёт текущий адрес сервера и может ответить. До этого ему некуда отправлять. Механизм — это аутентифицированный роуминг, а не сокращение «доверие при первом использовании», потому что пакет должен был быть действительным до того, как конечная точка сместилась.

Документация инструментов для wg описывает это обновление, и её стоит прочитать один раз, если вы собираетесь проектировать вокруг этого.

Трансляция с состоянием добавляет своё направление

Когда UDP проходит через транслирующий шлюз, шлюз создаёт состояние для первого увиденного пакета. RFC 4787 описывает это поведение и тайм-ауты, которые в конце концов удаляют сопоставление. Неактивное сопоставление истекает, и входящий пакет с дальней стороны тогда отбрасывается шлюзом ещё до того, как достигнет хоста. Туннель, работавший всё утро, перестаёт отвечать после обеда именно по этой причине.

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

Настройте то направление, которое действительно нужно

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

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

Сравните состояние до и после триггера

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

wg show
conntrack -L -p udp 2>/dev/null | grep 51820
tcpdump -ni any -c 20 'udp port 51820'
ip route get 198.51.100.7

Запишите столбец конечной точки и запись трансляции вместе. Сторона без конечной точки и шлюз без сопоставления — две разные неисправности. Только одну из них исправляет keepalive.

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