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

Что закрепляет правило и что возвращает DNS

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

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

Три способа, которыми ломается совпадение

Очевидный случай — переехавший сервис: оператор дал ему новый адрес, DNS последовал, а исключение нет. Второй — семейство адресов, которое правило никогда не покрывало, что бывает, когда имя начинает возвращать записи AAAA, которые исключение только для IPv4 игнорирует. Третий — различие resolver: внутренний и публичный вид могут возвращать разные адреса для одного имени, поэтому адрес, который использует приложение, не тот, с которым вы тестировали.

Кешированное соединение — четвёртый вариант, и его легко пропустить. Приложение может до сих пор говорить с адресом, который разрешило час назад, а значит, свежий запрос и его открытый socket расходятся в том, где живёт сервис.

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

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

getent ahosts api.example.com
dig +short api.example.com A
dig +short api.example.com AAAA
ss -tnp | grep -F api
ip route get 203.0.113.40

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

Почините механизм, а не симптом

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

Если проект опирается на статический список, дайте ему владельца и план обновления. В плане должно быть сказано, как обнаруживается адрес, кто утверждает изменение и как быстро список должен последовать за миграцией провайдера. Список без владельца — это будущий сбой с известной причиной.

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

Проверьте обе половины замысла

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

curl -sv --max-time 5 https://api.example.com/ -o /dev/null 2>&1 | grep -E 'Trying|Connected to'
curl -sS --max-time 5 https://public.example/ -o /dev/null -w '%{http_code}\n'

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

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