DNS-сервер на Linux: BIND и Unbound для локальной зоны itpages.lab.local
Пока в сети пять хостов, можно жить на файле hosts и адресах по памяти. Когда их пятьдесят, без своего DNS начинается хаос: у каждого сервера свой список имён, при смене адреса приходится обходить все машины, а внутренние сервисы приходится открывать по IP. Собственный DNS-сервер решает это раз и навсегда: единая зона itpages.lab.local, прямое и обратное разрешение, кеширование для внешних запросов и контроль над тем, что видят клиенты.
В Linux для этого есть два основных инструмента, и они решают разные задачи. BIND (named) это полноценный авторитетный сервер: он хранит зоны, умеет передавать их на вторичные серверы и динамически обновляться. Unbound это быстрый кеширующий резолвер: он не хранит зоны как главный источник, зато отлично и безопасно отвечает на запросы клиентов. Мы соберём классическую связку на хосте srv-dns01 (192.168.10.2): BIND отвечает за зону itpages.lab.local, а Unbound принимает запросы клиентов, отправляет локальные в BIND, а внешние разрешает сам с кешированием.
Если сеть небольшая, достаточно одного BIND, который умеет и то и другое. Но разделение ролей даёт свои плюсы: резолвер на отдельном порту можно ограничить и мониторить независимо, а авторитетный сервер не подвергается рискам открытой рекурсии.
Шаг 1. Планируем зону и имена
Перед установкой определи, что именно будем хранить. Наша зона и записи:
- srv-dns01.itpages.lab.local: 192.168.10.2
- srv-dhcp01.itpages.lab.local: 192.168.10.3
- srv-linux01.itpages.lab.local: 192.168.10.10
- rtr-edge01.itpages.lab.local: 192.168.10.1
- ntp.itpages.lab.local: CNAME на srv-linux01
Одно предупреждение про имя зоны. Суффикс .local зарезервирован за multicast DNS (mDNS, Avahi, Bonjour), и на некоторых системах запросы к нему уходят не на DNS-сервер. Для лаборатории это терпимо, а для боевой сети лучше брать поддомен собственного настоящего домена, например lab.itpages.it.com. Мы оставляем itpages.lab.local по условию, но если клиенты на Linux с включённым avahi не видят имена, отключи mDNS-разрешение в nsswitch.conf.
Шаг 2. Установка и настройка BIND
Ставим пакеты. В Debian и Ubuntu сервис называется bind9, в RHEL это named.
sudo apt install bind9 bind9-utils bind9-dnsutils
# RHEL / Rocky: sudo dnf install bind bind-utils
sudo systemctl enable --now named
BIND будет слушать нестандартный порт 5353 на localhost, чтобы порт 53 отдать Unbound. Основные параметры:
# /etc/bind/named.conf.options
options {
directory "/var/cache/bind";
listen-on port 5353 { 127.0.0.1; };
listen-on-v6 { none; };
allow-query { 127.0.0.1; };
recursion no;
allow-transfer { none; };
dnssec-validation no;
};
Теперь объявляем зоны. Прямая зона для имён и обратная для адресов:
# /etc/bind/named.conf.local
zone "itpages.lab.local" {
type master;
file "/etc/bind/zones/db.itpages.lab.local";
allow-update { none; };
};
zone "10.168.192.in-addr.arpa" {
type master;
file "/etc/bind/zones/db.192.168.10";
};
Обрати внимание на имя обратной зоны: октеты сети записываются в обратном порядке, 192.168.10 превращается в 10.168.192.in-addr.arpa. Это одна из самых частых ловушек новичков.
Шаг 3. Файлы зон
Файл прямой зоны содержит запись SOA (параметры зоны), NS и сами хосты. Серийный номер в SOA обязан увеличиваться при каждой правке, иначе вторичные серверы не заметят изменений. Удобный формат: дата и два знака счётчика.
; /etc/bind/zones/db.itpages.lab.local
$TTL 3600
@ IN SOA srv-dns01.itpages.lab.local. admin.itpages.lab.local. (
2026101301 ; serial
3600 ; refresh
900 ; retry
604800 ; expire
300 ) ; negative cache TTL
IN NS srv-dns01.itpages.lab.local.
rtr-edge01 IN A 192.168.10.1
srv-dns01 IN A 192.168.10.2
srv-dhcp01 IN A 192.168.10.3
srv-linux01 IN A 192.168.10.10
ntp IN CNAME srv-linux01
Точка в конце имени критически важна: без неё BIND допишет имя зоны, и получится srv-dns01.itpages.lab.local.itpages.lab.local. Это вторая по частоте ошибка после серийного номера.
; /etc/bind/zones/db.192.168.10
$TTL 3600
@ IN SOA srv-dns01.itpages.lab.local. admin.itpages.lab.local. (
2026101301 3600 900 604800 300 )
IN NS srv-dns01.itpages.lab.local.
1 IN PTR rtr-edge01.itpages.lab.local.
2 IN PTR srv-dns01.itpages.lab.local.
3 IN PTR srv-dhcp01.itpages.lab.local.
10 IN PTR srv-linux01.itpages.lab.local.
Прежде чем перезагружать сервис, проверь синтаксис конфигурации и зон. Эти проверки находят почти все опечатки:
sudo mkdir -p /etc/bind/zones
sudo named-checkconf
sudo named-checkzone itpages.lab.local /etc/bind/zones/db.itpages.lab.local
sudo named-checkzone 10.168.192.in-addr.arpa /etc/bind/zones/db.192.168.10
sudo systemctl restart named
dig @127.0.0.1 -p 5353 srv-linux01.itpages.lab.local +short
Шаг 4. Unbound как резолвер для клиентов
Теперь ставим Unbound, который принимает запросы от всей сети. Свою зону он пересылает в BIND, а всё остальное разрешает сам или через внешние DNS.
sudo apt install unbound
# RHEL / Rocky: sudo dnf install unbound
# /etc/unbound/unbound.conf.d/lab.conf
server:
interface: 192.168.10.2
interface: 127.0.0.1
port: 53
access-control: 127.0.0.0/8 allow
access-control: 192.168.10.0/24 allow
access-control: 192.168.20.0/24 allow
access-control: 0.0.0.0/0 refuse
hide-identity: yes
hide-version: yes
cache-min-ttl: 60
prefetch: yes
do-not-query-localhost: no
domain-insecure: "itpages.lab.local"
private-domain: "itpages.lab.local"
local-zone: "10.168.192.in-addr.arpa" transparent
stub-zone:
name: "itpages.lab.local"
stub-addr: 127.0.0.1@5353
stub-zone:
name: "10.168.192.in-addr.arpa"
stub-addr: 127.0.0.1@5353
forward-zone:
name: "."
forward-addr: 1.1.1.1
forward-addr: 9.9.9.9
Что здесь происходит. Блок access-control ограничивает круг клиентов: все остальные получают отказ, и открытый резолвер из нашего сервера не получится. Две секции stub-zone направляют запросы по нашей зоне и обратной зоне в локальный BIND на порт 5353. Секция forward-zone с точкой отправляет всё остальное на внешние публичные резолверы. Параметр do-not-query-localhost: no нужен, потому что иначе Unbound откажется обращаться к 127.0.0.1, а BIND у нас именно там. Если хочется, чтобы Unbound разрешал внешние имена сам, начиная с корневых серверов, просто удали секцию forward-zone.
sudo unbound-checkconf
sudo systemctl restart unbound
sudo systemctl enable unbound
sudo ss -ulpn | grep ':53'
На Ubuntu порт 53 по умолчанию занят systemd-resolved (stub-слушатель на 127.0.0.53). Он не мешает, если Unbound слушает конкретные адреса, но если возникает ошибка «address already in use», отключи stub-слушатель: DNSStubListener=no в /etc/systemd/resolved.conf.
Шаг 5. Проверка и подключение клиентов
Проверяем работу цепочки с любого хоста сети:
dig @192.168.10.2 srv-dhcp01.itpages.lab.local +short
dig @192.168.10.2 -x 192.168.10.10 +short
dig @192.168.10.2 ntp.itpages.lab.local
dig @192.168.10.2 example.com +short
dig @192.168.10.2 example.com | grep 'Query time'
Первые три команды проверяют нашу зону, четвёртая внешнее разрешение, а при повторе пятой видно эффект кеша: второй запрос должен отвечать за 0 мс. Затем настрой клиентов. В Netplan это блок nameservers с адресом 192.168.10.2 и поисковым доменом, в NetworkManager параметры ipv4.dns и ipv4.dns-search, а чаще всего эти значения раздаёт DHCP-сервер, о котором мы поговорим в следующей статье.
resolvectl status | grep -E 'DNS Servers|DNS Domain'
getent hosts srv-linux01
ping -c 2 srv-linux01
Если короткое имя srv-linux01 разрешается, значит поисковый суффикс работает правильно.
Ещё два практических совета. Сначала добавь вторичный DNS-сервер: даже в лаборатории стоит поднять второй экземпляр, настроив на нём зону как type slave (или secondary), а на главном указав allow-transfer и also-notify. Падение единственного DNS означает падение почти всего. Затем включи логирование запросов только на время диагностики (unbound-control set_option log-queries: yes или rndc querylog on), потому что постоянное логирование нагружает диск и накапливает персональные данные.
Шаг 6. Вторичный сервер и обслуживание зоны
Единственный DNS-сервер это единая точка отказа для всей сети. Добавим второй экземпляр на отдельном хосте и настроим автоматическую передачу зоны. На главном сервере разрешаем передачу и оповещение:
# в блоках zone на srv-dns01 добавить:
allow-transfer { 192.168.10.4; };
also-notify { 192.168.10.4; };
notify yes;
На вторичном сервере (допустим, srv-dns02 с адресом 192.168.10.4) зоны объявляются как вторичные, и он сам забирает данные при каждом увеличении серийного номера:
zone "itpages.lab.local" {
type secondary;
primaries { 192.168.10.2 port 5353; };
file "/var/cache/bind/db.itpages.lab.local";
};
Не забудь добавить в зону запись NS и A для второго сервера и указать оба адреса клиентам. Проверка репликации сводится к сравнению серийных номеров:
dig @192.168.10.2 itpages.lab.local SOA +short
dig @192.168.10.4 itpages.lab.local SOA +short
sudo rndc zonestatus itpages.lab.local
Для повседневной работы пригодятся несколько команд управления. Перечитать одну зону без остановки сервиса можно через rndc reload itpages.lab.local, а сбросить кеш резолвера Unbound командами unbound-control flush_zone itpages.lab.local или unbound-control flush_all. Статистику запросов удобно смотреть так:
sudo unbound-control stats_noreset | egrep 'total.num.queries|cachehits|cachemiss'
sudo rndc status
Чтобы unbound-control работал, включи в конфиге блок remote-control: control-enable: yes и один раз выполни unbound-control-setup. Отношение cachehits к общему числу запросов показывает эффективность кеша: в стабильной сети оно легко превышает 70 процентов, а резкое падение говорит о том, что клиенты запрашивают много уникальных имён или кто-то сканирует вашу сеть.
Шаг 7. Безопасность и производительность резолвера
Резолвер это одна из самых атакуемых служб в сети, поэтому после запуска стоит затянуть несколько параметров. В Unbound включи ограничение частоты запросов и защиту от слишком больших ответов, а также отключи ненужное:
server:
ratelimit: 1000
ip-ratelimit: 100
harden-glue: yes
harden-dnssec-stripped: yes
use-caps-for-id: yes
unwanted-reply-threshold: 10000
num-threads: 2
msg-cache-size: 64m
rrset-cache-size: 128m
so-reuseport: yes
Параметр ip-ratelimit ограничивает число запросов с одного адреса в секунду, так что взбесившийся клиент или сканер не положит сервис. Размеры кешей подбирай по объёму памяти: rrset-cache обычно в два раза больше msg-cache. Число потоков ставь равным количеству ядер, которые готов отдать резолверу.
Для контроля работоспособности добавь простую проверку, которую можно подключить к мониторингу: она запрашивает локальное имя и внешнее и сообщает о сбое.
dig +short +time=2 +tries=1 @192.168.10.2 srv-linux01.itpages.lab.local || echo FAIL-local
dig +short +time=2 +tries=1 @192.168.10.2 example.com || echo FAIL-external
Раздельные проверки помогают быстро понять, что сломалось: если падает только внешнее имя, проблема в пересылке на публичные DNS или в выходе в интернет, а если локальное, то BIND или зона.
Частые ошибки и как их избежать
- Забытый серийный номер и точка в конце имени. После правки зоны сервер отдаёт старые данные, потому что serial не увеличен, или записи превращаются в имена с двойным суффиксом, потому что не поставлена конечная точка. Всегда гоняй
named-checkzoneдо перезагрузки, используй формат serial ГГГГММДДNN и перезагружай зону командойrndc reload itpages.lab.local. - Открытая рекурсия для всего мира. Если оставить резолвер доступным из интернета, его быстро найдут и используют для DDoS-усиления. Ограничь
access-controlв Unbound иallow-recursion/allow-queryв BIND только своими подсетями, а на внешнем интерфейсе закрой UDP и TCP порт 53 файрволом. - Неверная обратная зона и конфликт с systemd-resolved. Обратная зона записывается в обратном порядке октетов, а на Ubuntu порт 53 занят stub-слушателем. Результат: PTR не находятся, а сервис не стартует. Проверяй
dig -xиss -ulpn | grep :53сразу после настройки, а не когда пожаловались пользователи.
Что дальше
Администрирование Linux — это набор связанных навыков, вот соседние темы серии: