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

Метка не путешествует вместе с запросом

Метки пакетов живут в записи conntrack и на пакете в полёте. Правило REDIRECT завершает этот полёт: полезная нагрузка передаётся локальному сокету, а исходная запись сделала своё дело. То, что после этого покидает маршрутизатор, — это новый сокет, созданный процессом резолвера, с адресом самого маршрутизатора в качестве источника.

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

Два разговора действительно разделяют имя и время, и именно это заставляет их выглядеть как один поток, когда быстро просматриваешь запись.

Где пересылка превращается в локальное разрешение

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

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

sudo nft list ruleset | grep -n -B2 -A4 'dport 53'
sudo iptables -t nat -L -n -v | grep -E ':53|dns'
sudo ss -lunp | grep ':53'

Эти выводы по порядку отвечают на три вопроса: что переписывает запрос, что его принимает и какой процесс владеет слушающим сокетом. Как только владелец известен, политике к upstream есть где жить.

Дайте резолверу собственную политику выхода

Upstream, привязанный к домену, — самый чистый инструмент, когда туннель нужен только некоторым именам. dnsmasq принимает строку server, ограниченную доменом, с необязательным интерфейсом или адресом источника, который направляет запрос из выбранного устройства. Именно это направление заставляет второй разговор следовать за VPN.

server=/corp.example/10.30.0.53@tun_work
log-queries

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

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

Что не может сохранить общий кеш

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

Ответ из кеша тоже не является доказательством насчёт нового пути. Ответ может прийти из памяти, пока маршрут к upstream остаётся сломанным.

Правила для порта 53 ничего не говорят о приложениях, которые говорят DNS поверх HTTPS или поверх TLS. Эти запросы никогда не достигают перенаправления, поэтому политика, построенная только на порте 53, описывает подмножество трафика и должна быть задокументирована как подмножество.

Проверяйте второй разговор, а не первый

Проверьте на одном имени, которое вы контролируете, и наблюдайте за обеими сторонами. Запись на стороне клиента доказывает, что запрос покинул LAN; только сторона маршрутизатора доказывает, куда он отправился дальше.

sudo tcpdump -ni tun_work udp port 53
ip route get 10.30.0.53
dig +short corp.example @10.30.0.53

Очищайте кеш перед каждой попыткой, потому что тёплый ответ скрывает сломанный upstream. Повторите через TCP, затем повторите с выключенным туннелем, чтобы увидеть, что делает резолвер, когда его предпочтительный путь исчез. Именно это последнее поведение решает, является ли проект fail-closed или просто удачным.

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