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

Симптом: второй клиент тихо побеждает

OpenVPN: параметр redirect-gateway def1 не перезаписывает 0.0.0.0/0. Он добавляет 0.0.0.0/1 и 128.0.0.0/1: вместе они охватывают каждый адрес IPv4, и каждый префикс длиной бьёт обычный маршрут по умолчанию. Исходный маршрут по умолчанию остаётся в таблице и перестаёт использоваться для обычного трафика, а маршрут к хосту VPN-сервера не даёт туннелю перехватить собственные внешние пакеты. Оба клиента делают так, а второй просто устанавливает свою пару позже.

Поэтому первый туннель кажется мёртвым, а не отключённым. Его интерфейс поднят, канал управления жив, и трафик приложений больше до него не доходит. Обратная жалоба имеет тот же отсутствующий push: телефон, который подключается, но ничего не отправляет, обычно говорит с сервером, который никогда не объявлял redirect-gateway, поэтому устройство сохраняет свой маршрут по умолчанию и достигает только пула.

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

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

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

ip route show
ip route get 198.51.100.24
route print -4
Find-NetRoute -RemoteIPAddress 198.51.100.24

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

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

Windows: не позволяйте одному профилю владеть маршрутом по умолчанию

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

Set-VpnConnection -Name "Contoso" -SplitTunneling $true
rasdial Contoso

Split tunneling это меняет: в таблицу попадают только префиксы, отправленные сервером, что оставляет маршрут по умолчанию у первого туннеля. Проект всё равно терпит неудачу, если оба сервера отправляют маршрут по умолчанию, потому что тогда побеждает последнее соединение и никакая настройка на стороне клиента этого не меняет. Исправьте push на стороне сервера или примите одного владельца.

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

Linux: держите второй туннель вне главной таблицы

Запуск двух экземпляров openvpn --config на одной машине помещает оба клиента в одну таблицу, и три варианта разделяют их с растущей изоляцией. Самый лёгкий — вообще не давать второму клиенту редактировать таблицу: route-nopull с явными маршрутами или скрипт route-up, который устанавливает только задуманное. Тогда второй туннель несёт ровно названные вами префиксы и ничего больше. Тогда вы решаете, какие префиксы куда идут, вместо того чтобы обнаруживать это потом.

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

ip rule add from 10.44.7.0/24 table 100
ip route add default dev tun1 table 100
sysctl net.ipv4.conf.all.rp_filter

Сетевые пространства имён — самый тяжёлый и самый чистый вариант. Запустите второй клиент внутри ip netns, дайте ему пару veth и войдите в это пространство имён, чтобы им пользоваться. Помехи исчезают, потому что у каждого пространства имён свои таблицы вместо одной общей, и это стоит дополнительной настройки, когда оба туннеля должны оставаться поднятыми неделями.

По приложениям, и цепочка как отдельная проблема

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

Цепочка — это другая операция, чем запуск двух туннелей рядом. Связать A поверх B значит, что адрес внутреннего сервера должен быть доступен изнутри внешнего туннеля. Внешний клиент также не должен принимать зашифрованные пакеты внутреннего клиента за утечку. Один клиент внутри виртуальной машины — грубый, но надёжный вариант; оба на одном хосте требуют описанной выше работы с маршрутами плюс внешний путь к внутреннему серверу, который никогда не входит во внутренний туннель.

Запишите владельца маршрута по умолчанию, список префиксов каждого туннеля и путь DNS. Эти три факта превращают следующий инцидент в справку, а не в расследование.

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