Почему частные DNS-имена не работают на клиентах VPN и где это проверить
Частные имена разрешаются, только когда клиент доверяет вашему резолверу, суффикс ограничен, а ответ может вернуться через туннель.
Вы открываете ноутбук в гостиничном номере, запускаете клиент, вводите имя внутреннего файлового сервера и не получаете ничего. Туннель сообщает о подключении, а ping до адреса в той же подсети по-прежнему отвечает, значит сам транспорт в порядке. Отказывает только имя, и этот разрыв сужает проблему до двух мест: какой резолвер спросил клиент и может ли полученный ответ вернуться через туннель. Исправьте одно и пропустите другое, и сбой просто переедет.
Прежде чем править любой профиль, проверьте, что клиент делает с именем. В Linux resolvectl query files.corp.example показывает, через какое соединение и какой резолвер получен ответ. В Windows Resolve-DnsName files.corp.example показывает сервер, который ответил, и адрес, который он вернул.
Прочитайте ответ, прежде чем что-то менять
Если имя возвращается как NXDOMAIN за несколько миллисекунд, запрос так и не дошёл до вашей зоны, и ответил какой-то публичный или гостиничный резолвер. Если оно возвращается после долгой паузы, что-то по пути отбрасывает UDP-порт 53, и клиент в итоге перешёл на запасной вариант. Если ответ приходит с правильным адресом, но соединение всё равно застревает, разрешение в порядке, и ваша проблема в маршрутизации или в самой службе.
Это разделение на три экономит час догадок. Оно также говорит, стоит ли вообще трогать VPN: имя, которое разрешается в правильный частный адрес, уже доказало путь резолвера, поэтому оставшаяся работа на пути данных.
Раздайте резолвер изнутри туннеля
Клиенты делятся на две группы. Одной сервер может задать резолвер при установке соединения; другая хранит настройку в локальном файле, который ни один шлюз не может изменить удалённо.
OpenVPN в первой группе. На сервере push "dhcp-option DNS 10.8.0.53" и push "dhcp-option DOMAIN corp.example" идут по каналу управления, но только если в конфигурации клиента pull всё ещё включён. Клиент, отредактированный вручную и несущий pull-filter ignore "dhcp-option", их никогда не увидит, и эта единственная строка объясняет удивительно много случаев вида «сервер это проталкивает, но мой ноутбук не согласен».
WireGuard во второй группе, потому что в протоколе вообще нет согласования параметров. Резолвер и домен поиска попадают в собственный блок [Interface] клиента как DNS = 10.8.0.53, corp.example. wg-quick применяет это через resolvconf или systemd-resolved, а в Windows официальный клиент ставит правила NRPT, которые существуют только пока туннель поднят: Get-DnsClientNrptPolicy перечисляет их, что делает его быстрым способом увидеть, применял ли клиент когда-либо ваш файл.
IPsec/IKEv2 посередине. Плагин attr от strongSwan может вернуть DNS-сервер, взятый из пула, и клиент Windows обычно его принимает, но профиль, который закрепляет собственный сервер имён, побеждает всё, что предложит шлюз. На клиенте Linux эквивалентный ручной шаг — resolvectl dns wg0 10.8.0.53, затем resolvectl domain wg0 '~corp.example'; оба исчезают вместе с интерфейсом, что как раз то, что нужно для настройки, привязанной к туннелю.
Ограничьте суффикс, вместо того чтобы проглатывать каждый запрос
Направить весь список резолверов клиента на ваш внутренний сервер легко и обычно неправильно. Тогда каждое публичное имя разрешается через выход вашего центра обработки данных, CDN возвращают далёкие адреса, и один сбой резолвера уносит с собой и просмотр пользователя.
Отправляйте внутрь только частный суффикс. В systemd-resolved домен, начинающийся с ~, служит только для маршрутизации, поэтому ~corp.example означает, что запросы под этим суффиксом идут на серверы этого соединения, а всё остальное продолжает использовать физическую сеть. Windows делает то же самое с правилами NRPT, ограниченными пространством имён, а macOS — с дополнительным файлом резолвера в /etc/resolver/.
Мультикаст-имена заслуживают собственного ответа. Имена .local по замыслу link-local (RFC 6762), и интерфейс tun точка-точка не несёт эту мультикаст-группу, поэтому добавление публичного резолвера их не опубликует, а запрос к внутреннему unicast-резолверу возвращает NXDOMAIN. Отражатель вроде avahi в режиме отражения может соединить два сегмента, но это решение на стороне сервера, которого ни один клиент не может потребовать. Если принтер или NAS должен быть доступен по имени, дайте ему запись A в своей зоне.
Докажите, что ответ может вернуться
Правильный ответ, указывающий на недостижимый адрес, выглядит точно как проблема резолвера, пока вы не проверите маршрут. Спросите ядро, куда пойдёт ответ: ip route get 10.20.5.20 называет интерфейс и, что полезнее, исходный адрес, который он использует.
Следите за самим адресом резолвера. Клиент с DNS = 10.8.0.53 рядом с AllowedIPs = 10.20.0.0/16 отправляет запросы через физическую сетевую карту, где они тихо умирают, потому что резолвер не находится ни в одном префиксе, который заявляет туннель. Это несовпадение — самая частая односоставная причина отказа во всей этой области, и оно невидимо, пока вы не сравните настройку DNS с разрешёнными префиксами строка за строкой.
Перекрывающиеся диапазоны вызывают обратный отказ. Ответ правильный, маршрут существует, и он указывает на неверный хост, потому что 192.168.0.0/24 дома конкретнее маршрута туннеля в тот же диапазон. Перенумеруйте одну сторону или протолкните более узкий маршрут, затем подтвердите межсетевой экран и обратный путь хоста службы; заметки strongSwan о перенаправлении охватывают сторону sysctl этого пути.
Сертификаты — это отдельная проверка подлинности, которую люди списывают на DNS. Если браузер сообщает о несовпадении имени вместо ошибки разрешения, поиск уже сработал, и исправлять нужно SAN в сертификате. Проверяйте HTTPS по имени, никогда по голому IP.
Проверяйте заново с клиента, а не со шлюза
Проверяйте внутри приложения, на которое жалуется пользователь, потому что поиск из командной строки не доказывает, что браузер идёт тем же путём. Безопасный DNS Chrome и network.trr.mode в Firefox могут полностью обойти системный резолвер, и когда они это делают, частное имя либо отказывает, либо утекает к публичному резолверу. Сравнение resolvectl query с запущенным вручную dig @10.8.0.53 files.corp.example скажет, какой из двух случаев произошёл.
Затем отключитесь и подключитесь заново, прежде чем объявить это исправленным. Настройки, заданные вручную на интерфейсе, исчезают вместе с интерфейсом, а профиль, который выглядел правильным только из-за забытого локального изменения, откажет при следующей загрузке.
Короткий контрольный список для следующей машины: имя разрешается через резолвер туннеля, суффикс ограничен, а не глобален, адрес резолвера входит в разрешённые префиксы, хост из ответа отвечает на порту службы, и сертификат покрывает имя, которое ввёл пользователь.