Имена клиентов OpenVPN в dnsmasq: скрипт или статические записи?
Адреса клиентов меняются, имена служб — нет. Создавайте имена клиентов из сеанса VPN, закрепляйте имена служб в drop-in файле, а затем заставьте клиентов использовать его.
Почти на каждом небольшом шлюзе OpenVPN возникают две проблемы с именами. К подключённому ноутбуку не обратиться по имени из его сертификата, а у служб за туннелем вообще нет имён. Они выглядят как одна проблема и требуют противоположного подхода, потому что один адрес меняется с каждым сеансом, а другой нет. Решите, какой тип имени вы добавляете, прежде чем что-либо править.
Решайте по имени, а не по шлюзу
Проверка проста: меняется ли этот адрес, когда клиент переподключается? Если да, имя нужно создавать из сеанса VPN. Если нет, оно относится к статической записи, которую можно прочитать и просмотреть.
| Name kind | Source of truth | Where it lives |
|---|---|---|
| Connected client | The address OpenVPN just assigned | Generated hosts file, rewritten per session |
| Service behind the tunnel | A fixed address you chose | Static address= drop-in |
| Anything with a certificate name | The certificate, not the pool | Either, but the name must match the certificate |
Их смешение — обычная ошибка. Служба, закреплённая в созданном файле, исчезает при первой же перезаписи скриптом, а клиент, записанный в статическую запись, указывает на того, кто держал этот адрес последним. Обе ошибки остаются незаметными, пока имя не разрешится в неверную машину. К тому моменту тот, кто разбирается, думает, что сломан DNS, а не файл.
Динамический путь: learn-address и пул
OpenVPN знает назначение, так пусть он вам его и сообщит. Эти параметры сервера делают назначение устойчивым и пригодным для скриптов:
client-config-dir /etc/openvpn/ccd
ifconfig-pool-persist /etc/openvpn/ip-pool 60
script-security 2
learn-address /etc/openvpn/learn-address.sh
learn-address запускает ваш скрипт каждый раз, когда адрес добавляется, обновляется или удаляется, и передаёт операцию, адрес и общее имя. Минимальный скрипт добавляет по одной строке на клиента и убирает её при удалении:
#!/bin/sh
HOSTS=/etc/openvpn/hosts.lan
case "$1" in
add|update) echo "$2 $3" >> "$HOSTS" ;;
delete) sed -i "/^$2 /d" "$HOSTS" ;;
esac
kill -HUP $(cat /var/run/dnsmasq.pid)
На устройстве tun второй аргумент — это туннельный адрес клиента, поэтому строку можно использовать напрямую. На устройстве tap OpenVPN вместо этого передаёт MAC-адрес клиента, потому что сегмент tap может нести и другие протоколы помимо IPv4, и тогда адрес нужно найти в файле пула по общему имени, прежде чем что-либо писать.
Чётко понимайте, что такое созданный файл: снимок того, кто подключён прямо сейчас. Имена исчезают при отключении, и это именно то, что нужно для ноутбуков, перемещающихся между сетями, и именно то, чего не нужно для сервера, работающего всю ночь.
Статический путь: address= и расширение домена
Направьте dnsmasq на созданный файл вместо системного файла hosts и дайте ему домен, чтобы короткие имена разворачивались в полные:
domain=corp.example
no-hosts
addn-hosts=/etc/openvpn/hosts.lan
listen-address=127.0.0.1,10.8.0.1
bind-interfaces
Если domain задан, строка вида 10.8.0.6 alice отвечает и для alice, и для alice.corp.example. Держите listen-address только на шлюзе туннеля и loopback, потому что резолвер, случайно опубликованный на интерфейсе LAN, — это служба, которую вы не собирались предлагать.
Службы, которые никогда не приходят из пула, получают собственные записи. Поместите их в отдельный drop-in файл в каталоге конфигурации дистрибутива, чтобы обновление пакета не могло их перезаписать:
address=/files.corp.example/10.8.0.20
address=/db.corp.example/10.9.0.5
Передайте резолвер, иначе ничего не произойдёт
Резолвер, который никто не спрашивает, ничего не меняет. Сервер должен объявить его:
push "dhcp-option DNS 10.8.0.1"
push "dhcp-option DOMAIN corp.example"
Клиенты Windows применяют оба параметра сами. Большинство клиентов Linux — нет: им нужна поддерживаемая интеграция с резолвером, например хук update-resolv-conf дистрибутива или обработка DNS в NetworkManager, либо вручную заданные домен поиска и сервер имён. Проверяйте фактический резолвер клиента, а не предполагайте, что push соблюдён, потому что проигнорированный push выглядит точно так же, как сервер, который не отвечает.
Проверьте на трёх уровнях
Сначала спросите сервер напрямую, затем клиент, затем приложение:
dig +short @10.8.0.1 alice.corp.example
getent hosts files.corp.example
Если прямые запросы отвечают, а приложение нет, виновата передача клиенту, а не зона. Если имя разрешается, а соединение всё равно не устанавливается, имя в порядке, и следующее, на что смотреть, — маршрут или межсетевой экран.
Запишите обе категории рядом с конфигурацией: какие имена создаются, какие закреплены и с каким сертификатом каждое должно совпадать. Следующий человек, добавляющий сервер, скопирует первый найденный шаблон.