OpenVPN & SplitDNS в Linux, часть 1
В прошлом выпуске журнала "Мурзилка" я рассматривал варианты виртуальных TUN-адаптеров для работы с OpenVPN под Windows и вскользь затронул тему Split DNS. Например, когда нужно IP-адреса внутрикорпоративных ресурсов разрешать через "внутренние" же DNS-сервера, а все остальные — через "внешние". С неких пор под форточками этот вопрос решается просто: достаточно поставить драйвер от специального WinTun-адаптера имени WireGuard, который прикидывается 100-гигабитным, поэтому все DNS-запросы после его подключения пойдут именно через него. А как с этим дела обстоят в Linux?
Сперва немного матчасти.
С точки зрения Linux-а механизм разрешения доменных имен ничем не отличается, например, от разрешения имен пользователей в UID-ы или названий сетевых протоколов в их номера. Данный функционал реализован в библиотеке "libc" и является крайне примитивным. В том плане, что "из коробки" в нём нет ни кеширования, ни каких-то "хитрых" алгоритмов опроса, а уж тем более Split DNS дык и подавно. Отправной точкой конфигурации является файл "/etc/nsswitch.conf". В нём говорится где надо искать и в каком порядке. Как правило, в нём присутствует строка вида "hosts: files dns", что означает "сначала ищи в файле /etc/hosts, затем на серверах, перечисленных в /etc/resolv.conf".
Как я уже упоминал, никакого локального кеширования этот клиент не умеет. По этой причине Linux-овая машина может создавать весьма существенную нагрузку на DNS-сервера если, к примеру, на ней крутится какой-нибудь условный PHP-FPM, внутри скриптов которого прописано подключение к базе данных не по IP-адресу, а по доменному имени.
Дальше, как водится, началось строительство башни из костыльной_кости™. Исторически в разные времена практиковались различные решения. Но сейчас, в 2023м году, пока что остановились на systemd-resolved. Надолго ли, неизвестно. Это отдельный демон, который реализует разновсяческие штуковины, связанные с работой DNS-клиента и управляется посредством запросов через D-Bus. Таким образом с ним хорошо умеет общаться другой демон по имени NetworkManager, который отвечает за настройку сети. В последних версиях systemd-resolved обучен трюкам на тему Split DNS в том числе.
Сам systemd-resolved может отдавать информацию libc-ресолверу двумя разными способами: "традиционным" через UDP:53 на 127.0.0.53 и "продвинутым" посредством библиотеки "libnss-resolve", если таковая имеется в системе.
Теперь практика.
Как водится, одну и ту же задачу обычно можно решать множеством разных способов. Поскольку Linux представляет из себя грёбаный конструктор кишками наружу, то для реализации озвученной в заголовке поста темы существует как минимум четыре разных варианта.
- Запускать OpenVPN через интерфейс NetworkManager-а (GUI либо CLI). Тогда последний автомагически передаст информацию о DNS в systemd-resolved, откуда её потом заберёт libc-ресолвер.
- Запускать OpenVPN напрямую лапами (или через systemctl), но при этом предусмотреть в конфиге вызов специально обученного shell-скрипта, который через D-Bus будет дёргать systemd-resolved, далее всё то же самое что и в п.1.
- То же самое, что пункт 1, но вместо systemd-resolved использовать dnsmasq, подключив его как plug-in для NetworkManager-а. Такой подход позволяет создавать более хитровы*****ые конфигурации, чем с использованием systemd-resolved, но требует большего умственного напряжения сидящего за консолью.
- Использовать только dnsmasq в standalone-режиме и вообще не связываться ни с NetworkManager, ни с systemd-resolved. Вариант "на любителя", но тоже вполне имеет право на существование. Основной и главный недостаток: если речь идёт о ноутбуке, который постоянно "путешествует" и подключается к разным сетям с использованием DHCP, то править конфиг dnsmasq-а лапками всякий раз при перемещении в другую сеть несколько утомляет.
Продолжение следует (надеюсь).