Откуда появилась проблема
Многие из вас пользуются VPN подключением к рабочей локальной сети из дома.
Благодаря этому, подключив VPN вы работаете с рабочей сетью «как будто находясь в ней».
Как выглядит типичная настройка в linux?
В /etc/resolv.conf прописывается (при помощи openresolv или NetworkManager)
search local.company.name
nameserver 10.0.20.186
Где local.company.name — домен компании, а 10.0.20.186 — ip адрес локального (в рабочей сети) DNS сервера.
Как выглядит работа?
root@t510 0 ~ % ping -c 1 cdash-master
PING cdash-master.local.company.name (10.0.20.237) 56(84) bytes of data.
64 bytes from 10.0.20.237: icmp_seq=1 ttl=63 time=214 ms
...
Что произошло?
- search добавил к cdash-master суффикс local.company.name
- DNS сервер в рабочей сети преобразовал cdash-master.local.company.name в 10.0.20.237
Всё прекрасно работает, пока у вас VPN подключение… одно.
Мне захотелось подключать одновременно два VPN и прописывать два DNS — один для рабочей сети, другой для локальной сети на hetzner, где крутятся мои виртуальные машины.
Казалось бы, что может быть проще? Однако путь к решению был долгим и извилистым.
Подробное описание результата
Рабочая сеть
Локальная подсеть — 10.0.20.0/24 (т.е. 10.0.20.* — из подсети)
Домен — *.local.company.name
DNS сервер — 10.0.20.186
ping -с 1 somename
должен определить somename как somename.local.company.name, а somename.local.company.name превратить в IP адрес через DNS 10.0.20.186
Если VPN подключение выключено — имя не будет найдно.
Личный сервер на hetzner
С ним всё немного хитрее
Локальная подсеть — 10.10.10.0/24 (т.е. 10.10.10.* — из подсети)
Домен — *.my.domain
Локальный DNS сервер — 10.10.10.253
Сервер my.domain имеет белый IP адрес и доступен извне
На сервере стоит nginx, который проксирует запросы вида subdomain.my.domain машинам из локальной подсети.
С одной стороны, если пользователь зашёл браузером на subdomain.my.domain, то DNS сервера регистратора вернут белый IP адрес my.domain, браузер отправит по данному IP адресу HTTP запрос, и дальше nginx разберётся куда его проксироват.
С другой стороны, при включенном VPN мои подключения вида:
ssh subdomain
должны попадать напрямую на машину из локальной подсети.
Зачем?
Мне так удобно. Я один раз настроил рабочую машину, и прозрачно гуляю по рабочей сети и личной сети. Снаружи по *.my.domain пользователи видят ровно один сервер (который по совместительству и VPN) и к локальным машинам доступа не имеют. Я же могу напрямую подключать по ssh как к машинам из рабочей сети, так и из личной.
«Подожди», скажет мне юзер.
«Я просто пропишу /etc/resolv.conf»:
search local.company.name my.domain
«И всё будет работать.»
Не будет. Для каждой зоны — local.company.name и my.domain требуется прописать свой DNS сервер. Через resolv.conf, openresolv эта проблема не решается.
Решение
Мне пришлось несколько часов мучать гугль и знакомых админов, чтобы найти решение.
Мой пост призван сэкономить ваше время.
Итак, прежде всего нужно поставить bind. Рабочая машина у меня на Arch Linux:
pacman -S bind
systemctl enable named
Дальше настраиваем /etc/named.conf, добавляет туда:
zone "local.company.name" IN {
type forward;
forwarders { 10.0.20.186; };
};
include "/usr/share/openvpn/named.conf.my.domain";
zone "." IN {
type forward;
forwarders { 8.8.8.8; 8.8.4.4; };
};
Настраиваем NetworkManager либо редактируем /etc/resolv.conf. Результатом в обоих случаях должен быть:
root@t510 0 /usr/share/openvpn # cat /etc/resolv.conf
# Generated by NetworkManager
search local.company.name my.domain
nameserver 127.0.0.1
В openvpn скрипт для my.domain добавляем:
up "/usr/share/openvpn/update-dns-my.domain"
down "/usr/share/openvpn/update-dns-my.domain"
Добавляем скрипт /usr/share/openvpn/update-dns-my.domain который будет обновлять настройки bind и перезапускать его после установления VPN подключения:
#!/bin/bash
set -eu
NAME=/usr/share/openvpn/named.conf.my.domain
TEMPLATE=${NAME}.template
function restart_bind ()
{
/usr/bin/systemctl restart named
}
case $script_type in
up)
cat ${TEMPLATE} > ${NAME}
restart_bind
;;
down)
cat /dev/null > ${NAME}
restart_bind
;;
esac
Добавляем /usr/share/openvpn/named.conf.my.domain.template
zone "my.domain" IN {
type forward;
forwarders { 10.10.10.253; };
};
Как это работает?
При установлении VPN подключения к личному серверу:
- openvpn запускает скрипт /usr/share/openvpn/update-dns-my.domain
- скрипт в файл /usr/share/openvpn/named.conf.my.domain записывает содержимое /usr/share/openvpn/named.conf.my.domain.template
- скрипт перезапускает bind
- bind вычитывает настройки зоны my.domain из обновлённого файла
- зона my.domain теперь ресолвится DNS сервером из локальной сети (доступен через VPN)
При отключении VPN подключения
- openvpn запускает скрипт /usr/share/openvpn/update-dns-my.domain
- скрипт очищает содержимое /usr/share/openvpn/named.conf.my.domain
- скрипт перезапускает bind
- bind вычитывает настройки зоны my.domain из обновлённого файла (забывает про зону)
- зона my.domain теперь ресолвится общедоступными DNS
Как это выглядит на практике?
root@t510 0 /usr/share/openvpn # systemctl start named # не забываем включить bind
root@t510 0 /usr/share/openvpn # systemctl start openvpn@waltham # рабочая сеть
root@t510 0 /usr/share/openvpn # systemctl start openvpn@hetzner # личная сеть
root@t510 0 /usr/share/openvpn # ping -c 1 cdash-master
PING cdash-master.local.company.name (10.0.20.237) 56(84) bytes of data.
64 bytes from 10.0.20.237: icmp_seq=1 ttl=63 time=214 ms
...
root@t510 0 /usr/share/openvpn # ping -c 1 db
PING db.my.domain (10.10.10.200) 56(84) bytes of data.
64 bytes from 10.10.10.200: icmp_seq=1 ttl=63 time=233 ms
...
root@t510 0 /usr/share/openvpn # systemctl stop openvpn@hetzner
root@t510 0 /usr/share/openvpn # ping -c 1 db
PING db.my.domain (white-ip-address) 56(84) bytes of data.
64 bytes from my.domain (white-ip-address): icmp_seq=1 ttl=55 time=124 ms
...
root@t510 0 /usr/share/openvpn # systemctl start openvpn@hetzner
root@t510 0 /usr/share/openvpn # ping -c 1 db
PING db.my.domain (10.10.10.200) 56(84) bytes of data.
64 bytes from 10.10.10.200: icmp_seq=1 ttl=63 time=118 ms
...
Забавно, что для получения такого простого поведения пришлось проделать столько работы.