NAT решает, какой адрес источника ваш VPN-сервер видит на самом деле
Адрес вашего ноутбука никогда не доходит до VPN-сервера. NAT переписывает его на последнем переходе, а IP-адрес туннеля выдаётся позже. Как сравнить оба конца.
Вы представляете, как VPN-сервер читает адрес вашего ноутбука из пакета и записывает его в журнал. На самом деле он читает то, что последний переход NAT поместил во внешний заголовок, а на телефоне, используемом как точка доступа, это часто адрес, разделяемый с сотнями других абонентов. Собственный адрес вашей машины 192.168.x.x никогда не пересекает интернет в этом заголовке, а адрес туннеля сервер выдаёт уже после установления соединения. Поэтому, когда журнал сервера и ip addr на ноутбуке расходятся, ни один из них не ошибается; они описывают разные уровни одной сессии.
Три адреса, и только один из них в журнале
Путаница возникает из-за сведения трёх адресов к одному. У каждого свой владелец, он живёт в своём заголовке и доходит до сервера своим путём.
| Address | Who assigns it | Where it sits | Visible to the VPN server |
|---|---|---|---|
Device address, e.g. 192.168.1.24 | Your router via DHCP | Outer header, before translation | No, rewritten in place |
| Translated address and port | Router, ISP CGNAT, or both | Outer header, after translation | Yes, this is the tuple it logs |
Tunnel address, e.g. 10.8.0.6 | The VPN server itself | Inner header, after decapsulation | Yes, but it chose it |
Только средняя строка неизвестна в момент подключения. Сервер записывает её в таблицу партнёров, файл состояния или журнал потоков в зависимости от демона: WireGuard называет её конечной точкой, файл состояния OpenVPN помечает её как Real Address, а strongSwan сообщает её как удалённый хост IKE_SA. Это значение обновляется каждый раз, когда приходит пакет из другого источника, поэтому меняется без чьего-либо перезапуска.
Нижняя строка — это то, что люди ошибочно читают как утверждение об идентичности. Она отмечает место в конфигурации сервера, а не человека, потому что сервер выбрал её из своего пула. Два пользователя могут получить один и тот же адрес туннеля в разные дни, а один может держать два с двух устройств.
Почему записанный кортеж меняется, пока туннель остаётся поднятым
Трансляция адресов — это таблица с таймером, а не постоянная привязка. Домашний маршрутизатор хранит своё UDP-сопоставление ещё некоторое время после последнего пакета, шлюз оператора перерабатывает порты по своему расписанию, а телефон, переходящий с Wi-Fi на LTE, возвращается с совершенно другим источником. Значения conntrack по умолчанию в Linux — полезный ориентир: nf_conntrack_udp_timeout составляет 30 секунд, а потоковый вариант 120, так что тихий туннель может потерять сопоставление задолго до того, как пользователь это заметит.
Туннель выживает потому, что протокол аутентифицирует партнёра, а не адрес. WireGuard после рукопожатия не хранит состояния, поэтому любой аутентифицированный пакет обновляет сохранённую конечную точку, и перепривязка исправляется на следующем пакете; именно для этого существует persistent-keepalive = 25 — чтобы сопоставление оставалось тёплым во время простоя. --float в OpenVPN делает то же самое и позволяет аутентифицированному клиенту продолжить с нового адреса и порта. В сборках 2.4 и 2.5, где float по умолчанию отключён, перепривязка сбрасывает сессию, и клиент подключается заново.
IPsec добивается этого с большим числом движущихся частей. IKEv2 замечает трансляцию, сравнивая хеши источника и назначения в полезной нагрузке обнаружения NAT, переходит на инкапсуляцию UDP 4500 и отслеживает keepalive до нового кортежа. Когда keepalive блокируются, SA остаётся поднятым лишь на бумаге, а трафик в одном направлении тихо останавливается.
Что сервер может приписать, а что нет
Миф, который стоит убить, состоит в том, что трансляция скрывает вас от оператора. Она меняет метку, а не наблюдение. Шлюз за NAT оператора видит пространство 100.64.0.0/10 или один публичный адрес, разделяемый многими абонентами, и всё же может связать поток с субъектом сертификата, открытым ключом WireGuard, выданным адресом туннеля, счётчиками байтов и временным окном. Ничему из этого не нужен ваш адрес LAN.
Ограничения работают и в обратную сторону. Общий адрес в журнале указывает на шлюз, а не на домохозяйство; две сессии с одного и того же транслированного адреса на разных портах — это разные устройства; один и тот же адрес в два разных дня может принадлежать двум несвязанным абонентам, если порт был переработан. Сервер, записывающий только ключи партнёров, не может сопоставить их с клиентом без базы данных учётных записей, а сервер, записывающий только адреса, не отличит ноутбук от телефона за одним маршрутизатором.
Сравните оба конца вместо догадок
Чтобы выяснить, что произошло, захватите одно и то же соединение на обоих концах в один момент и сравните кортеж, а не адрес. На клиенте, до любой трансляции:
ss -uapn 'dport = :51820'
ip -4 route get 203.0.113.10
wg show wg0 endpoints
Затем прочитайте запись трансляции на маршрутизаторе или шлюзе CGNAT и то, что в итоге приходит на сервер:
conntrack -L -p udp --dport 51820
tcpdump -ni eth0 udp port 51820
Три схемы объясняют большинство расхождений. Порт источника на сервере отличается от локального порта, а назначение совпадает: обычная трансляция портов. Адрес источника на сервере принадлежит блоку ISP, который не ваш: как минимум один дополнительный уровень NAT. Пакеты прекращаются после периода простоя и возобновляются с новым портом: сопоставление истекло, а не сбой маршрутизации. Если метки времени расходятся более чем на секунду-две, вы сравниваете две разные попытки.
Где разница ломает всё
Сбои, похожие на отказы сервера, обычно начинаются здесь. Правило доступа, привязанное к вашему домашнему адресу, перестаёт работать после смены аренды у ISP. Правило fail2ban, совпадающее с транслированным адресом, блокирует всех абонентов за этим шлюзом сразу. Партнёр «сайт-сайт» с фиксированным удалённым адресом перестаёт обновлять ключи после смены мобильного канала. Протокол туннеля был в порядке; предположение о внешнем кортеже — нет.
Та же путаница видна и на стороне сервера. Если сам VPN-бокс стоит за NAT, нужны проброс порта и работающий путь пересылки; руководство strongSwan по пересылке покрывает этот случай для IPsec. Сервер, доступный только по частному адресу, никогда не видит транслированный кортеж, который представляют его клиенты, и это другая проблема, чем трансляция клиентов.
Пройдите эти четыре проверки по порядку и остановитесь на первой неудачной:
- Захватите на обоих концах одновременно; подтвердите, что порт источника, порт назначения и метки времени совпадают.
- Прочитайте в списке партнёров сервера адрес, который он сейчас держит, а не тот, который вы ожидали.
- Проверьте таблицу conntrack или NAT на запись и её оставшийся срок жизни.
- Подтвердите, что адрес интерфейса туннеля на клиенте совпадает с выданным сервером, а не с адресом LAN.
Если оба захвата совпадают по кортежу, а туннель всё равно не работает, трансляция — не ваша проблема; переходите к рукопожатию и к таблице маршрутизации внутри туннеля.