Настройка DNS-сервера BIND

BIND (Berkeley Internet Name Domain) — распространённый DNS-сервер. Его основной процесс называется named. BIND может быть авторитетным сервером, рекурсивным кеширующим резолвером, DNS-forwarder, primary/secondary-сервером и основой для split DNS.

DNS обычно использует UDP/53 для запросов и TCP/53 для больших ответов, DNSSEC и передачи зон. Для DNS нужно разрешать оба протокола.

Не открывайте рекурсию всему интернету: открытый резолвер может использоваться для DDoS-атак с усилением трафика.

Содержание


Основные понятия

Основные записи:

Тип Назначение Пример
A Имя → IPv4 web IN A 192.168.10.20
AAAA Имя → IPv6 web IN AAAA 2001:db8::20
CNAME Псевдоним www IN CNAME web
MX Почтовый сервер @ IN MX 10 mail
NS Сервер зоны @ IN NS ns1.example.internal.
PTR Адрес → имя 20 IN PTR web.example.internal.
TXT Текстовые данные @ IN TXT "value"
SRV Узел и порт сервиса _ldap._tcp IN SRV 10 0 389 ldap
CAA Разрешённый CA @ IN CAA 0 issue "letsencrypt.org"
SOA Параметры зоны обязательна в каждой зоне

Имя с CNAME не должно одновременно иметь другие записи.


Установка

Debian/Ubuntu

sudo apt update
sudo apt install bind9 bind9-utils dnsutils
sudo systemctl enable --now bind9

Конфигурация обычно расположена в /etc/bind/:

/etc/bind/named.conf
/etc/bind/named.conf.options
/etc/bind/named.conf.local
/etc/bind/named.conf.default-zones

Fedora/RHEL/Rocky/AlmaLinux

sudo dnf install bind bind-utils
sudo systemctl enable --now named

Основные пути: /etc/named.conf и /var/named/.

Arch Linux

sudo pacman -S bind
sudo systemctl enable --now named

Проверка:

named -V
systemctl status bind9   # Debian/Ubuntu
systemctl status named   # RHEL/Arch

Базовые параметры

Пример внутреннего резолвера /etc/bind/named.conf.options:

acl "trusted" {
    127.0.0.1;
    ::1;
    192.168.10.0/24;
};

options {
    directory "/var/cache/bind";

    listen-on { 127.0.0.1; 192.168.10.10; };
    listen-on-v6 { ::1; };

    recursion yes;
    allow-query { trusted; };
    allow-recursion { trusted; };
    allow-query-cache { trusted; };
    allow-transfer { none; };

    dnssec-validation auto;
    minimal-responses yes;
    version "not disclosed";
};

На RHEL значение directory обычно /var/named.

Для публичного сервера, выполняющего только авторитетные ответы:

options {
    recursion no;
    allow-query { any; };
    allow-query-cache { none; };
    allow-transfer { none; };
};

listen-on ограничивает адреса прослушивания, allow-query — допустимых клиентов, а allow-recursion — тех, кто вправе выполнять рекурсивные запросы.


Прямая зона

Допустим, DNS-сервер имеет адрес 192.168.10.10, а зона — example.internal.

В /etc/bind/named.conf.local:

zone "example.internal" {
    type primary;
    file "/etc/bind/zones/db.example.internal";
    allow-update { none; };
};

В старых версиях вместо type primary применяется type master. На RHEL обычно используют относительный путь file "db.example.internal";, соответствующий /var/named/db.example.internal.

Создание каталога в Debian/Ubuntu:

sudo install -d -o root -g bind -m 0750 /etc/bind/zones

Файл /etc/bind/zones/db.example.internal:

$TTL 3600
@   IN SOA ns1.example.internal. hostmaster.example.internal. (
        2026091801 ; serial
        3600       ; refresh
        900        ; retry
        1209600    ; expire
        300        ; negative cache TTL
)

    IN NS      ns1.example.internal.
    IN NS      ns2.example.internal.

ns1     IN A       192.168.10.10
ns2     IN A       192.168.10.11
@       IN A       192.168.10.20
www     IN A       192.168.10.20
api     IN A       192.168.10.21
mail    IN A       192.168.10.25
@       IN MX 10   mail.example.internal.
files   IN CNAME   www.example.internal.
_ldap._tcp IN SRV 10 0 389 ldap.example.internal.
ldap    IN A       192.168.10.30

В SOA адрес hostmaster.example.internal. означает hostmaster@example.internal. Формат serial часто делают YYYYMMDDNN. После каждого изменения статической зоны serial необходимо увеличить.


Обратная зона IPv4

Для сети 192.168.10.0/24 используется зона 10.168.192.in-addr.arpa:

zone "10.168.192.in-addr.arpa" {
    type primary;
    file "/etc/bind/zones/db.192.168.10";
    allow-update { none; };
};

Файл зоны:

$TTL 3600
@   IN SOA ns1.example.internal. hostmaster.example.internal. (
        2026091801 3600 900 1209600 300
)
    IN NS ns1.example.internal.
    IN NS ns2.example.internal.

10  IN PTR ns1.example.internal.
11  IN PTR ns2.example.internal.
20  IN PTR www.example.internal.
21  IN PTR api.example.internal.
25  IN PTR mail.example.internal.

Для публичных адресов обратную зону обычно делегирует владелец адресного блока или провайдер. Для IPv6 применяются зоны ip6.arpa, где шестнадцатеричные символы адреса записываются в обратном порядке.


Проверка конфигурации

Всегда проверяйте конфигурацию до reload/restart:

sudo named-checkconf
sudo named-checkconf -p
sudo named-checkzone example.internal /etc/bind/zones/db.example.internal
sudo named-checkzone 10.168.192.in-addr.arpa /etc/bind/zones/db.192.168.10

Применение:

sudo rndc reload
sudo rndc reload example.internal

Либо:

sudo systemctl reload bind9
sudo systemctl reload named

reload предпочтительнее restart: он не останавливает рабочий сервер.


Проверка запросов

dig @192.168.10.10 www.example.internal A
dig @192.168.10.10 www.example.internal A +short
dig @192.168.10.10 example.internal MX
dig @192.168.10.10 example.internal NS
dig @192.168.10.10 example.internal SOA
dig @192.168.10.10 -x 192.168.10.20
dig @192.168.10.10 example.internal SOA +tcp
dig example.com +trace

Важные флаги dig:

Дополнительные команды:

host www.example.internal 192.168.10.10
nslookup www.example.internal 192.168.10.10

Кеширующий сервер и forwarders

BIND может выполнять рекурсию самостоятельно либо пересылать запросы другим DNS-серверам:

options {
    recursion yes;
    allow-recursion { trusted; };
    allow-query-cache { trusted; };

    forwarders {
        192.0.2.53;
        198.51.100.53;
    };
    forward first;
};

Условная пересылка отдельной зоны:

zone "corp.example" {
    type forward;
    forward only;
    forwarders { 10.50.0.10; 10.50.0.11; };
};

Очистка кеша:

sudo rndc flush
sudo rndc flushname www.example.com

Secondary-сервер и передача зон

Primary:

acl "secondaries" { 192.168.10.11; };

zone "example.internal" {
    type primary;
    file "/etc/bind/zones/db.example.internal";
    allow-transfer { secondaries; };
    also-notify { 192.168.10.11; };
};

Secondary:

zone "example.internal" {
    type secondary;
    primaries { 192.168.10.10; };
    file "/var/cache/bind/db.example.internal";
};

Старые эквиваленты — type slave и masters. На RHEL для получаемых зон часто используют file "slaves/db.example.internal";.

Проверка AXFR:

dig @192.168.10.10 example.internal AXFR

AXFR раскрывает всю зону, поэтому allow-transfer нельзя задавать как any. Для дополнительной аутентификации передачи применяйте TSIG.

Создание TSIG-ключа:

sudo tsig-keygen -a hmac-sha256 secondary-key \
  | sudo tee /etc/bind/secondary-key.conf >/dev/null
sudo chown root:bind /etc/bind/secondary-key.conf
sudo chmod 0640 /etc/bind/secondary-key.conf

Подключение:

include "/etc/bind/secondary-key.conf";

Ограничение передачи ключом:

allow-transfer { key "secondary-key"; };

TSIG аутентифицирует сообщения, но не шифрует содержимое.


Split DNS через view

Split DNS возвращает разные данные внутренним и внешним клиентам:

acl "internal-networks" {
    127.0.0.1;
    10.0.0.0/8;
    192.168.0.0/16;
};

view "internal" {
    match-clients { internal-networks; };
    recursion yes;

    zone "example.com" {
        type primary;
        file "/etc/bind/views/internal/db.example.com";
    };
};

view "external" {
    match-clients { any; };
    recursion no;

    zone "example.com" {
        type primary;
        file "/etc/bind/views/external/db.example.com";
    };
};

Внутренняя зона может возвращать 10.20.30.40, внешняя — публичный адрес. Порядок view важен: используется первое совпадение. При использовании views все необходимые зоны следует объявлять в соответствующих views.


Динамические обновления

nsupdate позволяет менять записи без ручного редактирования зоны. Обновления нужно защищать TSIG.

sudo tsig-keygen -a hmac-sha256 ddns-key \
  | sudo tee /etc/bind/ddns-key.conf >/dev/null
sudo chmod 0640 /etc/bind/ddns-key.conf

Пример зоны:

include "/etc/bind/ddns-key.conf";

zone "example.internal" {
    type primary;
    file "/var/lib/bind/db.example.internal";
    update-policy {
        grant ddns-key zonesub ANY;
    };
};

Процесс named должен иметь право записи в каталог. Добавление записи:

nsupdate -k /etc/bind/ddns-key.conf
server 192.168.10.10
zone example.internal
update add test.example.internal. 300 A 192.168.10.50
send
quit

Удаление:

update delete test.example.internal. A
send

BIND сохраняет динамические изменения в .jnl. Перед ручным редактированием такой зоны используйте:

sudo rndc freeze example.internal
# Изменить файл и serial
sudo rndc thaw example.internal

DNSSEC

DNSSEC подтверждает подлинность и целостность DNS-данных, но не шифрует запросы.

Для рекурсивной валидации:

dnssec-validation auto;

Проверка:

dig @192.168.10.10 cloudflare.com A +dnssec
dig @192.168.10.10 dnssec-failed.org A

В первом случае валидированный ответ может содержать ad; тестовый домен с ошибкой DNSSEC обычно возвращает SERVFAIL.

Современные версии BIND поддерживают автоматизированное подписание:

zone "example.com" {
    type primary;
    file "/var/lib/bind/db.example.com";
    dnssec-policy default;
    inline-signing yes;
};

Для публичной зоны нужно передать DS-запись родительской зоне или регистратору. Ошибочная DS-запись делает домен недоступным для валидирующих клиентов, поэтому DNSSEC внедряют по проверенной процедуре и с учётом версии BIND.


Настройка DNS на клиентах

NetworkManager

nmcli connection show
sudo nmcli connection modify "System eth0" \
  ipv4.dns "192.168.10.10 192.168.10.11" \
  ipv4.ignore-auto-dns yes
sudo nmcli connection up "System eth0"

systemd-resolved

resolvectl status
sudo resolvectl dns eth0 192.168.10.10 192.168.10.11
sudo resolvectl domain eth0 example.internal
resolvectl query www.example.internal

~. в качестве домена направляет через интерфейс все DNS-запросы:

sudo resolvectl domain eth0 '~.'

/etc/resolv.conf

search example.internal
nameserver 192.168.10.10
nameserver 192.168.10.11

Этот файл часто управляется NetworkManager, systemd-resolved, DHCP или Netplan, поэтому ручные изменения могут быть перезаписаны.


Межсетевой экран

firewalld

sudo firewall-cmd --permanent --add-service=dns
sudo firewall-cmd --reload

UFW

sudo ufw allow from 192.168.10.0/24 to any port 53 proto udp
sudo ufw allow from 192.168.10.0/24 to any port 53 proto tcp

iptables

sudo iptables -A INPUT -p udp -s 192.168.10.0/24 --dport 53 -j ACCEPT
sudo iptables -A INPUT -p tcp -s 192.168.10.0/24 --dport 53 -j ACCEPT

Проверка сокетов:

sudo ss -lntup | grep ':53'

Журналы и управление rndc

sudo journalctl -fu bind9
sudo journalctl -fu named
sudo rndc status
sudo rndc reload
sudo rndc reconfig
sudo rndc zonestatus example.internal
sudo rndc flush

Временное логирование всех запросов:

sudo rndc querylog on
sudo rndc querylog off

Оно создаёт большую нагрузку и объём данных, поэтому не оставляйте его включённым без необходимости.

Пример отдельного журнала:

logging {
    channel default_log {
        file "/var/log/named/default.log" versions 5 size 10m;
        severity info;
        print-time yes;
        print-severity yes;
    };
    category default { default_log; };
    category security { default_log; };
};

Каталог должен существовать и быть доступен пользователю bind или named. На системах с SELinux восстановите контексты:

sudo restorecon -Rv /var/named /var/log/named

Диагностика

Если сервис не запускается:

sudo named-checkconf
sudo systemctl status bind9 --no-pager
sudo journalctl -u bind9 -b -n 100 --no-pager

Для named замените имя unit. Частые причины: пропущенная ;, неверный путь, ошибки прав, занятый порт 53, SELinux или AppArmor.

Кто использует порт 53:

sudo ss -lntup | grep ':53'
sudo lsof -nP -i :53

127.0.0.53:53 может принадлежать systemd-resolved; это не конфликт, если BIND слушает другой адрес.

SERVFAIL часто означает ошибку DNSSEC, недоступный upstream, незагруженную зону, неверное делегирование или проблему времени. NXDOMAIN означает отсутствие имени и может кешироваться согласно negative TTL.

Проверка трафика:

sudo tcpdump -ni any port 53
sudo tcpdump -ni any 'host 192.168.10.50 and port 53'

Если secondary не получает зону, проверьте TCP/53, allow-transfer, TSIG, serial, права каталога и адрес primary:

sudo rndc retransfer example.internal
dig @192.168.10.10 example.internal SOA +short
dig @192.168.10.11 example.internal SOA +short

Безопасное изменение зоны

  1. Создайте резервную копию.
  2. Измените записи и увеличьте serial.
  3. Проверьте зону и конфигурацию.
  4. Выполните reload.
  5. Проверьте SOA и изменённые записи на primary и secondary.
sudo cp -a /etc/bind/zones/db.example.internal{,.bak}
sudo named-checkzone example.internal /etc/bind/zones/db.example.internal
sudo named-checkconf
sudo rndc reload example.internal
dig @127.0.0.1 example.internal SOA
dig @127.0.0.1 newhost.example.internal A

Рекомендации


Краткая памятка

# Проверка
sudo named-checkconf
sudo named-checkzone ZONE FILE

# Управление
sudo rndc status
sudo rndc reload
sudo rndc reload ZONE
sudo rndc flush

# Запросы
dig @SERVER NAME A
dig @SERVER ZONE SOA
dig @SERVER -x IP
dig @SERVER NAME +tcp
dig DOMAIN +trace

# Диагностика
sudo journalctl -fu bind9
sudo journalctl -fu named
sudo ss -lntup | grep ':53'
sudo tcpdump -ni any port 53