Хитрости BIND. Берем контроль над DNS в свои руки

Хитрости BIND. Берем контроль над DNS в свои руки

Life-Hack [Жизнь-Взлом]/Хакинг

#Обучение

BIND был и остается одной из самых популярных реализаций сервера DNS. Долгое время он был фактическим стандартом и для ответственных (authoritative), и для кэширующих серверов. Более того, до двухтысячных годов все до единого корневые сервера работали именно на нем. В этой статье мы рассмотрим ряд не слишком известных, но полезных возможностей BIND.

То десятилетие с середины девяностых по середину двухтысячных вообще было временем фактических монополий. Веб-сервер — Apache HTTPD, почтовый сервер — Sendmail, ну а сервер DNS — BIND. С тех пор появилось множество альтернатив. Например, часть корневых серверов перешли на NSD.

Однако альтернативы старым проектам часто идут по пути специализации. NSD не кэширует запросы и предназначен только для держателей доменов — он идеально подходит для корневых серверов, но совершенно не подойдет для корпоративного или провайдерского сервера. Проект PowerDNS состоит из двух независимых частей: ответственный pdns и кэширующий pdns_recursor. Проект dnsmasq — чисто кэширующий сервер для малых сетей со встроенной реализацией DHCP.

В то же время BIND универсален. Возможно, это и позволяет ему оставаться популярным. Он может и отвечать за зоны, и кэшировать запросы, и хранить записи в реляционных БД. Это, может, и не лучший проект для каждой из задач в отдельности, но зато он решает их все одновременно с весьма неплохой производительностью. Таким способом не слишком большие компании и сети учебных и испытательных лабораторий вполне могут обойтись одним сервером DNS.

Проверяем синтаксис настроек

При работе с серверами вручную многие люди правят файл настроек, перезапускают сервис и бегут чинить, если он не запустился. И совершенно зря, потому что у многих проектов есть опции демона или отдельные утилиты для проверки синтаксиса! В их числе и BIND.

Утилиты named-checkconf и named-checkzone весьма полезны как при ручной настройке, так и при написании скриптов. Будем честными — грамматика файлов BIND не всегда интуитивна. С помощью этих утилит можно получить более-менее внятные сообщения об ошибках без перезапуска сервиса.

$ named-checkconf /tmp/new-named.conf || echo "Bad file"
/etc/named.conf:66: missing ';' before '}'
Bad file

$ named-checkzone example.com /var/named/data/example.com 
zone example.com/IN: NS 'ns1.example.com' has no address records (A or AAAA)
zone example.com/IN: not loaded due to errors.

Ускоряем загрузку зон

Текстовый формат зон BIND стал фактическим стандартом, и его поддерживают многие другие реализации, прежде всего чтобы упростить перенос существующих зон. Некоторые регистраторы даже позволяют редактировать и вносить записи в этом формате через веб-интерфейс. Формат www IN A 192.0.2.1 видели даже далекие от DNS люди.

INFO

Ключевое слово IN — это не предлог in, а сокращение от Internet. Протокол DNS и BIND появились до того, как исследовательские сети объединились в единый Интернет, поэтому в DNS до сих пор формально существуют классы записей IN (Internet) и CH (ChaosNet).

Однако этот формат не слишком прост для синтаксического разбора — и загрузка зон с большим числом записей может ощутимо замедлить запуск демона. Регистраторы доменов и хостеры нередко хранят записи в базе данных, что и быстрее, и проще в управлении из админки.

Но если проблема только в производительности, есть и более простой способ: использовать двоичные форматы файлов.

Конвертировать зону в двоичный формат можно командой named-compilezone. Формата два: map и raw. Формат map представляет собой простой дамп памяти. Он загружается быстрее всего, поскольку вообще не требует разбора, но не является переносимым — работает только в пределах одной архитектуры. Формат raw загружается чуть медленнее, но работает на любой машине.

Рассмотрим пример для формата map. Сначала нужно скомпилировать в него текстовый файл зоны.

$ sudo named-compilezone -Fmap -o /var/named/data/example.com.map example.com /var/named/data/example.com

Затем нужно соответствующим образом поправить описание зоны в /etc/named.conf. Формат файла с данными зоны не определяется автоматически, поэтому нам придется указать опцию masterfile-format.

zone "example.com" {
    type master;
    masterfile-format map;
    file "/var/named/data/example.com.map";
};

Ощутимая разница заметна только на очень больших файлах. Но если ты окажешься в такой ситуации, теперь ты знаешь, как из нее выйти.

Генерируем записи для диапазонов адресов

Часто в ответах на обратные запросы можно увидеть адреса вроде 192.0.2.10.customers.example.com. Такие записи удобны для админов других сетей — из вывода traceroute или tcpdump сразу понятно, какой компании принадлежит адрес.

Как они делаются? Можно сгенерировать их скриптом и записать в файл зоны, но в BIND для этой цели есть встроенный механизм — директива $GENERATE.

Для примера сгенерируем записи вида 192.0.2.X.users.example.com для диапазона адресов 192.0.2.1–100.

Сначала добавим обратную зону для сети 192.0.2.0/24 в /etc/named.conf.

zone "2.0.192.in-addr.arpa" {
        type master;
        file "/var/named/data/2.0.192.in-addr.arpa";
};

Затем создадим файл зоны.

$TTL 86400

@ IN SOA localhost. root.localhost. (
        2020071001 ; serial
        28800 ; refresh
        14400 ; retry
        3600000 ; expire
        86400 ; default TTL
)

2.0.192.in-addr.arpa.   IN  NS  ns1.example.com.

200 IN PTR jrandomhacker.users.example.com.

$GENERATE 1-100 $ PTR 192.0.2.$.users.example.com.

Применим изменения командой rndc reload или перезапуском сервиса. Для начала убедимся, что зона работает, и запросим PTR к настроенному вручную 192.0.2.200.

$ host 192.0.2.200 localhost
200.2.0.192.in-addr.arpa domain name pointer jrandomhacker.users.example.com.

Теперь проверим адрес, для которого нет явной записи.

$ host 192.0.2.76 localhost
76.2.0.192.in-addr.arpa domain name pointer 192.0.2.76.users.example.com.

Как видим, все работает как ожидалось.

Разберем директиву на части. Первый аргумент (1-100) — это диапазон значений счетчика. Символ $ при генерации записей заменяется на текущее значение счетчика. В нашем случае BIND сгенерирует 99 правил от $=1 до $=100.

Второй аргумент — имя записи. Для записей типа PTR это номер хоста в сети, и он совпадает со счетчиком, потому и просто $. Третий аргумент — тип записи. Наконец, последний аргумент — ее значение.

Увы, в BIND нет способа исключить из автоматической генерации адреса, для которых уже есть явные записи, так что для клиентов со статическими адресами нужно либо выделить отдельный диапазон, либо смириться с двумя записями для одного адреса — явной и автоматически сгенерированной (что, впрочем, не принесет никакого вреда).

Директива $GENERATE обрабатывается при загрузке зоны и генерирует все записи в памяти. Таким образом, с ее помощью можно сэкономить время админа и число строк в файле зоны, но требования к памяти останутся теми же.

Автоматически сгенерированные записи обычно ассоциируются с обратными зонами, но не ограничиваются ими.

Предположим, у тебя есть пять веб-серверов с адресами 203.0.113.10–15. Ты хочешь создать для них записи вида www[1-5].example.com. Это вполне можно сделать автоматически. Добавь вот такую запись в файл зоны example.com:

$GENERATE 1-5 www$ A 203.0.113.${10,0,d}

Здесь 10 это константа, которая добавляется к значению счетчика, 0 — число ведущих нулей в значении (мы генерируем IP-адрес и дополнение нулями нам не нужно), d — десятичный формат вывода.

Split DNS

Представь себе следующую распространенную ситуацию. Есть сеть, положим 10.0.0.0/24, в ней — веб-сервер 10.0.0.100 и рабочая станция 10.0.0.200. Есть маршрутизатор с внешним адресом 203.0.113.1. Порты 80/443 проброшены внутрь на 10.0.0.100.

Если создать запись вроде www.example.com. IN A 203.0.113.1, то запросы из внешних сетей на https://www.example.com будут отлично работать. Увы, без дополнительных мероприятий не будут работать запросы на этот адрес из внутренней сети.

Чаще всего эту проблему решают с помощью так называемого hairpin NAT. Об этом я уже когда-то писал. Решение с hairpin — приемлемое, но не идеальное. Трафик с этом случае идет через маршрутизатор, даже если оба хоста находятся в одном сегменте канального уровня и могли бы общаться напрямую.

Популярность решения с hairpin объяснима. Многие сети вообще не имеют своих серверов DNS, в этом случае других решений и нет. Даже если свои сервера есть, правило для hairpin сетевой администратор может настроить без взаимодействия с администратором сервера DNS. Тем не менее правильное решение этой задачи — использовать split DNS, то есть выдавать разный ответ в зависимости от того, кто спрашивает.

Рассмотрим пример настройки. Для простоты тестирования в пределах одной машины мы используем адрес 127.0.0.10 в качестве условной «внутренней сети» — под localhost отведена вся сеть 127.0.0.0/8, а не только общеизвестный 127.0.0.1.

Чтобы выдавать разные ответы, нужно создать несколько «представлений» (view) в /etc/named.conf. У каждого view должна быть прописана опция match-clients, которая принимает либо имя ACL, либо ключевое слово any.

Важный момент: если ты используешь split DNS, то все зоны нужно поместить в какое-то представление, даже если они используются только в одном представлении. Иначе демон откажется загружать настройки.

acl internal-network {
   127.0.0.10/32;
};

view "internal" {
        match-clients { internal-network; };

        zone "example.com" {
                type master;
                file "/var/named/data/example.com-internal";
        };
};

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

        zone "example.com" {
                type master;
                file "/var/named/data/example.com";
        };
};

Для тестирования воспользуемся утилитой dig, опять же из пакета BIND. Она позволяет указать адрес источника опцией -b. Убедимся, что записи для www.example.com в файлах example.com и example.com-internal разные, и выполним запросы сначала с 127.0.0.1, затем с 127.0.0.10.

$ cat /var/named/data/example.com | grep www

www IN A 203.0.113.1

$ cat /var/named/data/example.com-internal | grep www

www IN A 10.0.0.100

$ dig @127.0.0.1 www.example.com a

;; ANSWER SECTION: www.example.com. 86400 IN A 203.0.113.1

$ dig -b127.0.0.10 @127.0.0.1 www.example.com a

;; ANSWER SECTION: www.example.com. 86400 IN A 10.0.0.100

Как видим, во втором случае нам выдали в ответ внутренний адрес, что нам и нужно.

Заключение

Кто-то скажет, что свой сервер DNS больше не нужен и все можно сделать через админку регистратора. Но если у тебя сколько-нибудь сложная внутренняя сеть, это совершенно не так. Свой сервер может сделать сеть более удобной и управляемой, и BIND поможет тебе в этом.

Источник


Report Page