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

Префиксу нужен владелец, а не момент

Начните с того, что запишете требование как предложение с подлежащим: достигните 10.77.0.0/24 через офисный VPN, пока это соединение поднято. Это предложение называет префикс, соединение и условие и сразу исключает две популярные замены: хук входа и имя интерфейса вроде ppp0 или utun2.

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

Используйте поддерживаемый источник намерения маршрута

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

MechanismCan carry extra prefixesWho owns the lifetime
Gateway-pushed routesOften, if the gateway is configured for itThe connection
Managed profile settingsDepends on the client and profile formatThe profile
Provider tunnel settingsYes, as included and excluded routesThe provider process
Manual commandYes, onceNobody

Для разработчиков пакетных туннелей настройки туннеля IPv4 от Apple явно показывают включённые и исключённые маршруты, и у IPv6 есть свой аналог. Это API, которые использует провайдер, а не доказательство того, что каждая встроенная панель VPN предлагает администратору произвольный список маршрутов. Если текущая комбинация не может выразить нужные префиксы, следующий шаг — поддерживаемый путь настройки от оператора, а не другое приложение, установленное в надежде, что оно поведёт себя лучше.

Срок жизни включает очистку

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

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

Гонка при переподключении

Представьте быстрое переподключение. Сеанс A падает, сеанс B поднимается и устанавливает свой маршрут, а затем наконец запускается отложенная очистка A. Если эта очистка удаляет маршруты по префиксу, она забирает с собой действительное состояние B, и соединение, работавшее секунду назад, теперь маршрутизируется в никуда.

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

Что тестировать и чего маршрут не может

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

route -n get 10.77.0.5
netstat -rn -f inet

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

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

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