Сегодня погорим о DNS, сейчас, конечно, более актуальным является PowesDNS, хотя бы за счет того, что он имеет API и к нему есть не мало хороших веб интерфейсов, что делает его куда более привлекательным в сравнении с Bind, но и у PowerDNS есть свои недостатки. Сегодня мы поговорим именно о Bind DNS. И поговорим о том, как правильно его готовить. Рассматривать будем на примере контейнера docker. Я буду разварачивать его на Alpine Linux, т.к. его размер более привлекательный чем та же Ubuntu, но конфигурационные файлы подойдут под любую систему, разница лишь в относительных путях. Я набросал вот такой Dockerfile:
FROM alpine
WORKDIR /etc/bind
COPY named.conf /etc/bind/named.conf
RUN apk --update add bind bind-tools bind-dnssec-tools && \
rm -rf /var/lib/apt/lists/* && \
rm /var/cache/apk/*
EXPOSE 53
CMD ["/usr/sbin/named", "-c", "/etc/bind/named.conf", "-p", "53", "-u", "named", "-f"]
Здесь надо уделить внимание только на строку копирования файла конфигурации named.conf, в остальном я думаю не должно возникнуть вопросов, устанавливаем сам сервер, далее запускаем его и открываем 53 порт. Важно что для корректной работы необходимо открывать как TCP так и UDP порт. Теперь можно переходить непосредственно к самому файлу named.conf:
options {
directory "/var/bind";
listen-on { 127.0.0.1; };
listen-on-v6 { none; };
forwarders {
77.88.8.8; 8.8.8.8;
};
recursion yes;
allow-recursion {
any;
};
version "DNS";
notify no;
rate-limit {
responses-per-second 25;
window 5;
};
allow-query {
any;
};
allow-transfer {
none;
};
pid-file "/var/run/named/named.pid";
};
logging {
channel bind_stdout {
stderr;
severity info;
print-category yes;
print-severity yes;
print-time yes;
};
category default { bind_stdout; };
category update { bind_stdout; };
category update-security { bind_stdout; };
category security { bind_stdout; };
category queries { bind_stdout; };
};
zone "." {
type hint;
file "/usr/share/dns-root-hints/named.root";
};
include "/etc/bind/zone/main.conf";
Немного о директивах в этом файле, listen-on { 127.0.0.1; };
говорит о том какой адрес необходимо слушать серверу. В боевых условиях, где сервер будет обслуживать реальные зоны вы должны указать внешний адрес, либо установить значение в any. forwarders { 77.88.8.8; 8.8.8.8; }; если адрес к которому мы обращаемся не находиться в пределах обслуживаемых нами зон, то отдавать запросы следующим серверам, я указал dns сервера яндекса и гугла. Остается на ваше усмотрение, может быть вы добавите туда сервера AD.
recursion yes; allow-recursion { any; }; Разрешаем рекурсивные запросы, как раз то, о чем шла речь выше, то есть все клиенты которые будут обращаться к вашему серверу, если зона находиться за пределами будут перенаправлены. Здесь можно и в каких-то случаях нужно, перечислить сети и адреса которым рекурсия разрешена.
allow-query { any; }; allow-transfer { none; }; Мы разрешаем опрашивать наш сервер всем, в противном случае, ограничьте возможность делать запросы только вашими сетями. То же самое касается трансфера зон от мастера к слейву, но в нашем примере у нас только один сервер. По этому это значение none.
Все остальные директивы не столь значимы, можно интуитивно понять для чего они, а так же почитать официальную документацию. https://kb.isc.org/docs/aa-00851
Все готово, теперь нам необходимо создать наши зоны, об этом говорит последняя строка конфигурационного файла include «/etc/bind/zone/main.conf»;
zone "yakunin.local" {
type master;
file "/etc/bind/zone/yakunin.local.conf";
allow-query { any; };
};
zone "0.0.127.in-addr.arpa" {
type master;
file "/etc/bind/zone/0.0.127.in-addr.arpa.conf";
allow-query { any; };
};
Здесь мы как раз указываем зоны которые будем обслуживать, в качестве примера, я сделал домен yakunin.local, обратите внимание что ниже есть обратная зона, это зона адресов которые вы будете использовать что бы создавать PTR записи. По этому для домена мы заводим 2 зоны, прямую и обратную. Рассмотрим сначала прямую:
$TTL 3600
@ IN SOA ns1.yakunin.local. postmaster.yakunin.local. (
2021111502 ; Serial
3600 ; Refresh
900 ; Retry
2419200 ; Expire
3600 ) ; Negative Cache TTL
IN NS ns1.yakunin.local.
@ MX 10 mail.yakunin.local.
@ IN TXT "v=spf1 +a +mx ~all"
@ IN A 127.0.0.1
www IN A 127.0.0.1
ns1 IN A 127.0.0.1
mail IN A 127.0.0.1
Объясню немного, первая строка задает время жизни записей о зоне для других DNS серверов, другими словами, если вы выключите свой сервер, данные о вашей зоне будет доступно в интернете или на других локальных днс серверах ещё 3600 минут. Далее мы задаем параметры зоны, у нас один сервер, он же главный, по этому ns1.yakunin.local, bind не позволяет передавать символ @ так как он является якорем для домена, то есть указывая @ мы говорим это это и есть тот самый домен который описан. А строка postmaster.yakunin.local. это лишь интерпритация емайл адреса администратора домена, то есть, это эквивалентно postmaster@yakunin.local
Далее идет серийный номер записи, это один из важных моментов, по правилам саомго bind, этот параметр должен быть равен ггггммдднн (год,месяц,день,номер_изменения записи) последнюю пару цифр вы должны менять всегда, когда вносите изменения в файл зоны (добавляете или удаляете запись и т.д.) каждое изменение это увеличение счетчика на один. Таким образом остальные сервера будут понимать что у вас произошло изменение и его необходимо «забрать».
Далее задаем NS (Name Server), в данном случае у нас только один сервер, по этому мы объявляем только одну запись со значением ns1.yakunin.local, если у нас несколько серверов мы должны перечислить и указать их все, каждый с новой строки. Далее мы объявляем какой почтовый сервер будет обслуживать наш домен и его приоритет. Так же добавим SPF запись для почтового домена. И в конце файла мы указываем записи для каждого поддомена. Первой строкой @ IN A 127.0.0.1 мы говорим что основной адрес нашего домена это 127.0.0.1, теперь все что будет пиговаться с маской, будет отдавать 127.0.0.1 ping *.yakunin.local, где * это любое значение, даже то, которое не описано в файле зон. Следующие записи указывают явно присвоение адреса к поддомену, то есть, у каждой записи может быть разный IP, которому сопоставлена запись. Наверное все это не сильно понятно, но если вы обратитесь к документации, то все будет более понятно. Теперь рассмотрим файл обратной зоны:
$TTL 3600
@ IN SOA ns1.yakunin.local. postmaster.yakunin.local. (
2021111505 ; Serial
3600 ; Refresh
900 ; Retry
2419200 ; Expire
3600 ) ; Negative Cache TTL
IN NS ns1.yakunin.local.
1 IN PTR yakunin.local.
Как ведите шапка файла сталось неизменной,что же тогда такое обратная зона? Когда мы делаем ping yakunin.local в ответ мы получаем IP адрес, то же самое и наоборот, то есть мы сделаем ping 127.0.0.1 то в после хост будет yakunin.local. Для чего это нужно? В первую очередь для построения почтовых серверов, если адрес не имеет обратной записи, то доверия к такому серверу будет куда меньше, а в некоторых случаях домены не имеющие обратной записи, вообще попадают сразу в спам. Какой принцип построения? В данном примере обслуживается 127.0.0.0/24, и из этой сети мы берем первый адрес 127.0.0.1 в обратном порядке это будет 1.0.0.127 и именно так, а не 1.0.0.721, то есть октет должен остаться таким же, но в зеркалом порядке. Строка: 1 IN PTR yakunin.local. горит что из сети 127.0.0.0/24, адрес 127.0.0.1 присвоен домену yakunin.local. А если бы надо было добавить ещё какой-то? Например адрес 127.0.0.2 домену ftp.yakunin.dev то строка была бы такой: 2 IN PTR ftp.yakunin.local. Надеюсь вы поняли смысл, так же при изменении этого файла вам необходимо увеличивать последние 2 числа серийного номера. Как и в случае с прямой зоной. Теперь когда у нас есть все файлы, мы можем сделать docker-compose.yaml:
version: "3.2"
services:
bind:
image: bind:alpine
container_name: bind
hostname: bind-dns
build: ./src
restart: always
volumes:
- ./zone:/etc/bind/zone
cap_add:
- NET_ADMIN
- SYS_ADMIN
ulimits:
memlock:
soft: -1
hard: -1
logging:
driver: "json-file"
options:
max-size: "1m"
max-file: 2
network_mode: host
После чего структура у вас должна получиться такой:
.
├── docker-compose.yaml
├── src
│ ├── Dockerfile
│ └── named.conf
└── zone
├── 0.0.127.in-addr.arpa.conf
├── main.conf
└── yakunin.local.conf
2 directories, 6 files
Можно запускать стек:
> docker compose up --build --detach
Обратите внимание что контейнер запускается в режиме сети — хост, то есть что бы сервер заработал убедитесь что никакие другие службы не занимают 53 порт на адресе 127.0.0.1 иначе вы получите ошибку. Например начиная с Ubuntu 20.04 в её составе работает unbound. По этому их необходимо отключить:
> sudo systemctl disable systemd-resolved.service
> sudo systemctl stop systemd-resolved.service
> sudo unlink /etc/resolv.conf
> sudo systemctl disable unbound.service
> sudo systemctl disable unbound-resolvconf.service
> sudo systemctl stop unbound.service
> sudo systemctl stop unbound-resolvconf.service
> sudo echo "nameserver 127.0.0.1" > /etc/resolv.conf
Теперь мы можем запускать наш стек и пробовать проверить работу сервера:
> sudo docker logs bind
25-Aug-2022 13:12:10.940 zoneload: info: zone 0.0.127.in-addr.arpa/IN: loaded serial 2021111505
25-Aug-2022 13:12:10.940 zoneload: info: zone yakunin.local/IN: loaded serial 2021111502
25-Aug-2022 13:12:10.944 general: notice: all zones loaded
25-Aug-2022 13:12:10.944 general: notice: running
25-Aug-2022 13:12:10.972 dnssec: info: managed-keys-zone: Key 20326 for zone . is now trusted (acceptance timer complete)
25-Aug-2022 13:12:11.060 resolver: info: resolver priming query complete
> ping yakunin.local
PING yakunin.local (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.027 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.103 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.078 ms
^C
--- yakunin.local ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2048ms
rtt min/avg/max/mdev = 0.027/0.069/0.103/0.031 ms
> nslookup yakunin.local
Server: 127.0.0.1
Address: 127.0.0.1#53
Name: yakunin.local
Address: 127.0.0.1
> nslookup 127.0.0.1
1.0.0.127.in-addr.arpa name = yakunin.local.
Как видим все работает. Попробуйте заменить домен на какой-то свой и добавить другие записи в зону. Для удобства я выложил все это в гит, можно склонировать репозиторий к себе и попробовать запустить. Кстати в примерах выше я использую docker compose plugin который теперь входит в ядро докера и его не надо устанавливать отдельным бинарным файлом как это было раньше. Почитайте об этом на официальном сайте докер. Ссылка на резпозиторий: https://git.yakunin.dev/yakunin/docker/-/tree/main/bind-alpine