Два восходящих канала, один туннель, и трафик выходит через тот, который вы не выбирали. Частные подсети доступны, журнал сообщает об успешном подключении, и именно измеряемый канал его несёт. Ничто в выводе OpenVPN не упоминает шлюз, потому что клиент никогда его не выбирал. Он открыл сокет к удалённому адресу и позволил ядру решить, как добраться до этого адреса.

Адрес туннеля — не конечная точка

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

Запись remote в профиле задаёт внешнее назначение, поэтому именно этот адрес нужно закрепить. Это также значение, которое меняется, когда имя хоста разрешается иначе или профиль перечисляет несколько remote.

Найдите адрес, к которому клиент действительно обратился

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

ss -tunp | grep -i openvpn
journalctl -u openvpn-client@work --no-pager | grep -i "Peer Connection Initiated"

Строка журнала указывает адрес и порт пира на момент подключения. Если она расходится с профилем, что-то перед клиентом переписывает назначение до начала соединения.

Проверьте и семейство адресов. Профиль с двумя стеками может подключаться по IPv6, тогда как ваше исключение покрывает только IPv4, и тогда трафик следует другому правилу, чем вы написали. То же касается прокси, отвечающего от имени конечной точки. В обоих случаях получается туннель, который работает, пока ваш маршрут ничего не делает.

Закрепите конечную точку и прочитайте порядок политики

Маршрут к хосту конкретнее любого маршрута по умолчанию. Поэтому /32 для адреса пира побеждает в таблице, которая его содержит. Обычная форма явно называет шлюз и устройство:

ip route add 203.0.113.40/32 via 192.168.10.1 dev eth0

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

ip rule show
ip route get 203.0.113.40

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

Когда конечная точка перемещается

Имя хоста, разрешающееся в несколько адресов, делает постоянный /32 недействительным, как только ответ меняется. Профиль с несколькими строками remote имеет ту же проблему, потому что клиент может приземлиться на любой из них. Обновляйте маршрут из собственных хуков жизненного цикла клиента, а не добавляйте вручную. Скрипт up, который читает согласованного пира и устанавливает маршрут к хосту, держит исключение в соответствии с реальностью, а соответствующий скрипт down удаляет его.

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

Держите внутреннюю политику отдельно

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

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

Подтвердите результат двумя независимыми наблюдениями: локальным адресом и устройством сокета и маршрутом, в который разрешается частное назначение. Когда оба соответствуют вашему замыслу, разделение реально. Руководство OpenVPN 2.6 описывает параметры remote и локальной привязки, которые определяют, к какому адресу обращается клиент.

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