Действительно ли трафик идёт через VPN? Как это проверить
Поиск маршрута, первый хоп, захват на физическом адаптере и проверка адреса снаружи отвечают на разные части вопроса.
Две неисправности выглядят одинаково со значка в системном трее: трафик, который входит в туннель, и трафик, который тихо его обходит. Клиент в обоих случаях показывает Connected, потому что Connected означает лишь, что интерфейс туннеля существует и клиент установил нужные ему маршруты. Использовал ли один пакет одного приложения эти маршруты — отдельный вопрос, на который отвечают в другом месте стека. Четыре измерения решают большую часть — поиск маршрута, первый хоп, захват на физическом адаптере и наблюдение снаружи — а представление сессии на сервере покрывает остальное.
Начните с маршрута, по которому пойдёт пакет
Таблица маршрутизации и есть решение, поэтому спросите её об одном адресате, а не читайте её целиком и гадайте. В Linux ip route get называет интерфейс и адрес источника, которые выбрало ядро:
ip route get 203.0.113.10
ip route get 10.20.5.7В Windows Find-NetRoute -RemoteIPAddress 203.0.113.10 полезнее, чем route print -4, по той же причине: он сообщает о победившем маршруте, а не обо всех существующих. Всё равно прочитайте таблицу один раз ради её строки 0.0.0.0. Пара /1 там означает, что клиент полного туннеля переписал маршрут по умолчанию: опция redirect-gateway def1 OpenVPN устанавливает 0.0.0.0/1 и 128.0.0.0/1 через туннель, что побеждает существующий 0.0.0.0/0 по самому длинному совпадению префикса, не удаляя физический маршрут по умолчанию. Поэтому два маршрута по умолчанию могут сосуществовать, а всё по-прежнему уходит в туннель.
Две детали делают таблицу лживой. Во-первых, смотрите на совпавший префикс, а не на число маршрутов по умолчанию, потому что победителя определяет метрика интерфейса или пара /1. Во-вторых, уже существующее соединение сохраняет путь, с которым было установлено; после подъёма туннеля открытая сессия может продолжать использовать старый маршрут, пока её не сбросят, и поэтому переподключение будто исправляет проблему маршрутизации. Работающий клиент — ps ax | grep openvpn — доказывает только то, что процесс запустился.
Проследите одно внутреннее и одно публичное имя
Поиск маршрута показывает намерение; путь показывает доставку. Проследите имя, которое обязано быть внутренним, и имя, которое не должно, пока вы подключены, и сравните первый хоп:
traceroute intranet.corp.example
traceroute 1.1.1.1Один и тот же первый хоп для обоих означает, что внутреннее имя так и не вошло в туннель и неисправность находится выше, обычно это имя, разрешающееся в публичный адрес, или маршрут, который так и не был установлен. Если внутренний трафик выходит через 10.8.0.1, а публичный через 192.168.1.1, разделённый туннель делает ровно то, для чего был настроен. След, который доходит до шлюза VPN и затем возвращает только тайм-ауты, обычно означает проблему обратного пути или межсетевого экрана сервера, а не отсутствующий маршрут пересылки.
Отфильтрованный ICMP делает эту проверку лживой: звёздочки на обоих следах выглядят как мёртвый туннель и обычно это межсетевой экран. Проверьте TCP-порт там, где можете — traceroute -T -p 443 в Linux — и помните, что след ничего не говорит о том, какой распознаватель ответил: имя может разрешиться локально и всё же быть доступным через туннель.
Захватывайте на физическом адаптере, а не на туннеле
Физическое звено — единственное место, которое показывает, что сеть действительно видит. При поднятом туннеле сессия к офису должна выглядеть как зашифрованная нагрузка, адресованная серверу VPN, а не как соединение с 10.20.5.7:
sudo tcpdump -ni eth0 udp port 53
sudo tcpdump -ni eth0 host 10.20.0.5Первый фильтр ловит разрешение имён, уходящее локально, и это самая частая и самая тихая утечка — страница всё равно загружается, распознаватель просто увидел запрос. Второй ловит обычный трафик к внутреннему адресу, который должен существовать только внутри туннеля. В Windows pktmon делает ту же работу без драйвера: pktmon filter add -p 53, затем pktmon start --capture --pkt-size 128, затем pktmon stop и pktmon format pktmon.etl -o capture.txt. Эти флаги немного различаются между сборками Windows, поэтому проверьте pktmon filter add /?, прежде чем доверять синтаксису.
Захватывайте физический интерфейс, чтобы найти утечки; захват на туннеле содержит пакеты, которые вы уже считаете присутствующими, а на сервере он содержит нечитаемые внешние пакеты, чей источник — шлюз. Счётчики байтов на любом интерфейсе описывают пакеты, а не программы; чтобы отнести трафик к процессу, нужен монитор, сопоставляющий сокеты с процессами.
Спросите внешний сервис, а затем спросите ещё раз для каждого приложения
Единственная проверка, которая доказывает выход, — это ответ снаружи: сервис, который возвращает увиденный адрес источника. Запустите его из оболочки, где можно выбрать интерфейс, и запустите дважды:
sudo curl --interface tun0 https://example.net/ip
sudo curl --interface eth0 https://example.net/ipРазные ответы дают доказательство обоих путей и способ проверить конкретное приложение: привяжите его, или запустите в сетевом пространстве имён с одним маршрутом, и посмотрите, какой адрес запишет дальний конец. Одинаковые ответы — это интересная неисправность: либо привязка не подействовала, либо что-то между хостом и дальним концом переписывает адрес источника до прибытия. В Windows curl --interface требует адрес адаптера, а не его имя.
Браузер, сообщающий адрес VPN, доказывает браузер. Почтовый клиент, менеджер пакетов, агент резервного копирования или второй профиль браузера могут каждый идти своим путём, особенно когда настройка прокси или маршрут разделённого туннеля покрывает один из них. Изменение адреса само по себе не доказательство утечки: балансировщик может ответить иначе в зависимости от увиденного выхода, поэтому сравнивайте записанный адрес источника, а не страницу.
Используйте представление сервера, чтобы подтвердить партнёра
Сервер отвечает на другой вопрос: подключён ли партнёр, сколько он перенёс и перезапускалась ли сессия. Партнёр, который активен на сервере, пока счётчики клиента почти не двигаются, означает, что трафик приложения не входит в туннель, что бы ни говорила таблица клиента. А партнёр, который передаёт много данных для пользователя, сообщающего, что ничего не работает, — это другое расследование.
Здесь таятся две ловушки. Маскированный туннель заставляет каждый поток выглядеть приходящим от шлюза, поэтому журналы и учёт по клиентам схлопываются в один адрес. А клиент рассматривается как локальный хост, поэтому вопрос, копирует ли кто-то файлы через VPN, решает трафик общего доступа к файлам на сервере или аудит хоста на сервере — наблюдение за туннелем даёт конечные точки и объём, а не имена файлов.
Отличите проектное решение от утечки
Не всё за пределами туннеля — утечка. Трафик локального звена и широковещательный остаётся локальным по проекту, принтер, найденный через multicast, продолжает объявлять себя в LAN, а DHCP обязан выходить локально. Приложение, привязанное к конкретному интерфейсу, намеренно обходит таблицу маршрутизации, и это меняет только правило межсетевого экрана, пространство имён или политический маршрут.
IPv6 заслуживает отдельной строки в контрольном списке. Туннель, несущий только IPv4, оставляет двухстековому хосту второй пригодный путь, и двухстековый адресат предпочтёт его — включая DNS, если распознаватель доступен по IPv6. Отключите IPv6 на адаптере или подтвердите, что туннель его несёт, прежде чем считать результат IPv4 полным.
Когда проверки расходятся, расхождение обычно называет неисправность:
| What you see | What it means | Next step |
|---|---|---|
| The route picks tun0 but the capture shows cleartext to 10.20.5.7 | Something bypassed the table: a bound app, a policy route or a session older than the tunnel | Reset the flow, check the app bind, check the ip rule list |
| The route picks eth0 for an internal name | The name resolves to a public address, or the route was never installed | Compare the resolved address with the routes the server pushes |
| The capture shows DNS to the LAN resolver | Resolver leak: data is tunneled, lookups are not | Point the resolver inside, or push it from the server |
| All four checks agree and one app still fails | Path selection is fine; suspect MTU, a per-app proxy or the far end | Test the same destination from a known-good client |
Работающий порядок: сохраните таблицу маршрутизации до и после подключения, проследите одно внутреннее и одно публичное имя, захватите DNS на физическом адаптере, верните адрес выхода из оболочки для конкретного приложения, затем прочитайте сервер ради объёма. Если первые четыре совпадают, туннель несёт то, что вы думаете, а оставшаяся проблема выше него. Если два из них расходятся, расходящаяся пара и есть неисправность, а не значок.