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

Начните с двух потоков, которые нужно разделить

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

В этом разборе предположим клиента на 10.90.0.10 через tun1, публичный адрес сервера 198.51.100.25 и нетронутую главную таблицу, маршрут по умолчанию которой указывает на обычный аплинк. Серверная сторона также должна принимать веб-службу на внутреннем пути и возвращать трафик туннельному источнику; отправка пакета на публичный адрес не отменяет ни слушателя, ни политику межсетевого экрана сервера.

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

Пометьте сокет, который несёт конверт

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

mark 42
route-noexec
disable-dco

Десятичное 42 — это шестнадцатеричное 0x2a, и именно шестнадцатеричную форму будут сопоставлять правила маршрутизации. Справка по link-options описывает mark как то, что применяется к зашифрованным исходящим пакетам в Linux, то есть ровно к тому сокету, чьим путём нужно управлять. Отключение разгрузки канала данных удерживает этот пример на пути пользовательского пространства, где поведение метки — то самое описанное.

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

Не держите маршрут хоста в главной таблице для 198.51.100.25, пока делаете это. Такой маршрут вновь вводит ровно то исключение, которое проект пытается сузить, и победит для потока HTTPS прежде, чем будет опрошена какая-либо из политик ниже.

Отправьте всё остальное в таблицу туннеля

Как только интерфейс туннеля существует и держит свой адрес, политика выглядит так:

ip -4 route add default dev tun1 src 10.90.0.10 table 47100
ip -4 rule add priority 20000 lookup main suppress_prefixlength 0
ip -4 rule add priority 20001 not fwmark 0x2a lookup 47100
ip -4 rule add priority 20002 not fwmark 0x2a unreachable

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

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

Держите эту защиту на месте между переподключениями и устанавливайте её вместе с остальной политикой, а не вручную. Правило, исчезающее вместе с интерфейсом, — не политика, а совпадение. Удаление политики при падении туннеля меняет режим отказа с «заблокировано» на «утекло», а устаревший маршрут туннеля, истекающий позже, объяснить гораздо проще, чем трафик, ушедший через не тот интерфейс.

Пусть обратный путь видит ту же метку

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

Исправление принадлежит владельцу межсетевого экрана: сохраните метку на исходящем соединении, восстановите её на соответствующих входящих пакетах и включите обработку меток с проверкой источника для нужного пути. Установка метки сокета в OpenVPN не делает ни одной из этих трёх вещей, а рецепт, перечисляющий только клиентские опции, неполон. Запросите эту интеграцию письменно, потому что это наименее заметная часть проекта и легче всего теряемая при пересборке.

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

Докажите это двумя поисками маршрута

Сравните, что решает ядро для двух контекстов, и читайте поля dev и src, а не код возврата:

ip -4 route get 198.51.100.25
ip -4 route get 198.51.100.25 mark 0x2a

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

Запишите, что проект не покрывает, прежде чем объявить его готовым. DNS, IPv6 и жизненный цикл правил требуют каждый своего решения, и это пример маршрутизации IPv4, а не полная конфигурация приватности. Его ценность — селектор: конверт получает исключение аплинка, а обычная служба по тому же адресу не обязана его наследовать.

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