Ниже описывается установка DNS сервера BIND (Berkeley Internet Name Domain) на AWS EC2 в VPC + два дополнительных инстанса в разных подсетях (А и В), после чего на нём реализуем следующее:
- DNS round-robin Load Balancer — будет распределять трафик по очереди на каждый из двух дополнительных интансов
- DNS network-based routing — тут задача интереснее:
- если запрос приходит из подсети A — возвращаем адрес из подсети A
- если запрос приходит из подсети B — возвращаем адрес из посети B
- всем остальным — возвращаем «общий» IP
Практического смысла во второй части особо нет, особенно если дело касается AWS, но для «поиграться» с DNS «маршрутизацией» на основе сети клиента — подойдёт.
Когда-то аналогично выполнялась настройка GeoIP маршрутизации через BIND — для второй части используем точно такой же подход: view
и match-clinets
.
Содержание
DNS AWS
Лирическое отступление, касающееся DNS network-based routing и AWS.
У Amazon для раздачи разных IP используется другой подход: в VPC создаётся свой DNS (если указана опция enableDnsSupport
, по умолчанию она True), и DNS запросы внутри этой VPC направляются на него (см. DNS Support in Your VPC и Amazon DNS Server).
Далее этот DNS указывается в resolv.conf
EC2 интанса самим AWS во время запуска EC2 в VPC:
Поэтому при запросе DNS с EC2 — будет использован приватный IP:
А «через мир», например DNS Google — внешний:
т.к. Google получает информацию о зонах с authority servers, которые для доменов Amazon-а являются… DNS серверами Amazon-а, которые и возвращают публичный IP.
Проверим: находим Authority Section для домена eu-west-1.compute.amazonaws.com, который содержит нашу запись ec2-34-253-58-71.eu-west-1.compute.amazonaws.com:
И проверяем значение IN A
, который отдаёт dns-external-master.amazon.com (и который в результате попадает в DNS Google и нашего провайдера):
Понятно, что запрос с EC2 напрямую (через @ns.name.tld) к тому же DNS вернёт тот же результат:
Создание сервисов
Для начала развернём тестовое окружение, которое будет включать в себя 3 EC2 — один для DNS, и два в роли клиентов, и VPC с тремя подсетями — для DNS и для клиентов.
Подсети все будут публичными, потому обойдёмся без NAT Gateway.
VPC
Создаём VPC:
Добавляем тег Name:
Проверяем:
Security Group
Обновляем Security Group этой VPC — открываем 22 TCP:
53 UDP и TCP для BIND отовсюду (т.к. мы будем обслуживать собственню зону, и нам надо будет её отдавать другим DNS):
Subnets
Создаём подсеть для ЕС2 с BIND:
Тег:
Аналогично добавляем ещё две подсети для клиентов:
Internet Gateway
Добавляем Internet Gateway:
Подключаем IGW к VPC:
Route Table
Добавляем таблицу маршрутизации:
Добавляем правило маршрутизации в эту таблицу — весь трафик к 0.0.0.0 через IGW, созданный ранеее:
Подключаем эту таблицу к подсетям:
EC2
Создаём инстансы.
Для BIND:
Клиент в посети А:
Клиент B:
Подключаемся, проверяем сеть:
ОК — можно приступать к установке BIND.
Установка BIND
Наш BIND будет авторитарным (authoritative) сервером для домена bindtest.rtfm.co.ua, и рекурсивным (или resolving) сервером для клиентов А и В.
«Поняслася!» (с)
Устанавливаем BIND:
Редактируем /etc/bind/named.conf.options
, добавляем ACL, который будет содержать сервера, с которых будут разрешены рекурсивные запросы (сам сервер, и клиенты А и В).
Перед options
добавляем ACL «trusted»:
acl "trusted" { 10.0.1.157; # current host 10.0.2.145; # ClientA 10.0.3.164; # ClientB };
В сам блок options
добавляем:
... recursion yes; # enables resursive queries allow-recursion { trusted; }; # allows recursive queries from "trusted" clients listen-on { 10.128.10.11; }; # ns1 private IP address - listen on private network only allow-transfer { none; }; # disable zone transfers by default ...
В конце добавляем логгирование, в результате весь файл сейчас выглядит так:
acl "trusted" { 10.0.1.157; # current host 127.0.0.1; 10.0.2.145; # ClientA 10.0.3.164; # ClientB }; options { directory "/var/cache/bind"; dnssec-validation auto; auth-nxdomain no; # conform to RFC1035 listen-on-v6 { any; }; recursion yes; # enables resursive queries allow-recursion { trusted; }; # allows recursive queries from "trusted" clients allow-transfer { none; }; # disable zone transfers by default allow-query { any; }; # disable built-in server information version none; hostname none; server-id none; }; logging { channel b_query { file "/var/log/bind9/query.log" versions 2 size 1m; print-time yes; severity info; }; category queries { b_query; }; };
Создаём каталог для логов:
Проверяем конфиг:
Добавляем BIND в автозапуск:
Перезапускаем сервис:
Проверяем:
Пробуем запрос:
Лог:
ОК, тут всё готово.
Настройка Authoritative сервера
Наш BIND работает на EC2, на IP которого через CNAME
направлены домены ns1-example.rtfm.co.ua и ns2-example.rtfm.co.ua (потребуется два, что бы добавить новый субдомен у регистратора):
Это будут Name Servers для нашего bindtest.rtfm.co.ua, над которым мы будем «проводить эксперименты».
Сами домены ns1-example.rtfm.co.ua и ns2-* обслуживаются серверами регистратора — Freehost.ua:
Добавление зоны в BIND
Редактируем /etc/bind/named.conf.local
, добавляем указание на домен (зону), который мы будем обслуживать:
zone "bindtest.rtfm.co.ua" { type master; file "/etc/bind/zones/db.bindtest.rtfm.co.ua"; };
Создаём каталог:
В нём создаём файл зоны — копируем файл /etc/bind/db.local
:
Редактируем /etc/bind/zones/db.bindtest.rtfm.co.ua
, меняем SOA с записи вида:
@ IN SOA localhost. root.localhost. (
На:
@ IN SOA bindtest.rtfm.co.ua. ns1-example.rtfm.co.ua. (
Тут bindtest.rtfm.co.ua — домен, который мы будем обслуживать, а ns1-example.rtfm.co.ua — FQDN нашего ЕС2 с BIND-ом, он и будет Start of authority для bindtest.rtfm.co.ua.
Обновляем остальную информацию, не забываем увеличивать поле Serial на единицу при каждому изменении.
Приводим его к виду:
$TTL 604800 @ IN SOA bindtest.rtfm.co.ua. ns1-example.rtfm.co.ua. ( 3 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ) ; Negative Cache TTL ; @ IN NS ns1-example.rtfm.co.ua. @ IN NS ns2-example.rtfm.co.ua. @ IN A 34.253.58.71
Проверяем конфиги:
Проверяем файл зоны:
Перезагружаем named
:
Пробуем получить информацию:
Добавляем у регистратора домена rtfm.co.ua запись bindtest.rtfm.co.ua с параметрами IN NS
ns1-example.rtfm.co.ua и ns2-example.rtfm.co.ua:
И пробуем получить информацию о домене «из мира»:
DNS Load Balancing
Идея простого DNS Load Balancing — вернуть клиенту несколько IP в ответ на запрос записи IN A
.
В списке IP, который будет возвращать BIND порядок IP меняется случайным образом — такой себе round-robin. Клиент первым делом попробует выполнить подключение к первому из полученных IP, и если это не сработает — он использует следующий из списка.
ОК, обновляем файл зоны:
$TTL 604800 @ IN SOA bindtest.rtfm.co.ua. ns1-example.rtfm.co.ua. ( 5 ; Serial 604800 ; Refresh 86400 ; Retry 2419200 ; Expire 604800 ) ; Negative Cache TTL ; @ IN NS ns1-example.rtfm.co.ua. @ IN NS ns2-example.rtfm.co.ua. @ IN A 34.253.58.71 @ IN A 34.240.126.223 @ IN A 34.242.8.220
Тут 34.242.8.220 — публичный IP Клиента А, а 34.240.126.223 — Клиента В, используем просто для примера.
Не забываем обновить Serial.
Проверяем файл зоны:
Пробуем получить IP:
Три раза получаем первыми разные IP — вот и простой DNS Load balancer.
Продемонстрируем с NIGNX — на каждом хосте устанавливаем его:
На каждом обновляем файл /var/www/html/index.nginx-debian.html
:
Открываем порт 80 в SecurityGroup:
Проверяем:
Гасим NGINX на одном из северов, например на ClientB:
Проверяем:
Главный недостаток DNS Load Balancer — что он не имеет никакой возможности узнать насколько загружен тот или иной хост из пула адресов, которые DNS возвращает клиенту, и даже не может проверить — жив ли он (например — отвечает ли NGINX на 80 порту), поэтому не стоит считать его fail-over механизмом.
Кроме того — DNS медленно реагирует на изменения в конфигурации: пока не истечёт TTL для зоны — другие DNS не обновят информацию в своих кешах и будут отдавать старые параметры.
DNS network-based routing
Следующая задача — обеспечить раздачу разных IP в зависимости от того — откуда пришёл клиент.
Если запрос приходит от ClientA (из подсети A) — отдаём ему IP 34.242.8.220, если ClientB — то 34.240.126.223, для всех остальных — 34.253.58.71.
Аналогичный подход используется в посте Debian: настройка GeoIP для BIND — создаём отдельные файлы зон для различных клиентов, и с помощью view
и оператора match-clients
выбираем какую зону кому отдавать.
Обновляем named.conf.options
— создаём access control list-ы для каждого клиента — подсетей, которые создавали:
... acl "subnet-a" { 10.0.2.0/24; }; acl "subnet-b" { 10.0.3.0/24; }; ...
Копируем файл зоны db.bindtest.rtfm.co.ua
— создаём два дополнительных файла — для клиентов из подсети А, и для клиентов из подсети В:
Теперь у нас тут есть:
Данные из файла db.bindtest.rtfm.co.ua
будем отдавать всем «внешним» клиентам, данные из db-net-a.bindtest.rtfm.co.ua
— клиентам из подсети А, данные из db-net-b.bindtest.rtfm.co.ua
— клиентам из подсети В.
Обновляем в них IN A
, оставляем по одной, и инкременим Serial:
Проверяем:
Обновляем name.conf.local
добавляем view
и условия:
view "network-A" { match-clients { subnet-a; }; recursion no; zone bindtest.rtfm.co.ua { type master; file "/etc/bind/zones/db-net-a.bindtest.rtfm.co.ua"; }; }; view "network-B" { match-clients { subnet-b; }; recursion no; zone bindtest.rtfm.co.ua { type master; file "/etc/bind/zones/db-net-b.bindtest.rtfm.co.ua"; }; }; view "NotMatched" { match-clients { any; }; recursion no; zone bindtest.rtfm.co.ua { type master; file "/etc/bind/zones/db.bindtest.rtfm.co.ua"; }; };
Во избежание ошибки:
Добавляем view
для всех зон в /etc/bind/named.conf.default-zones
:
view localhost_resolver { // prime the server with knowledge of the root servers zone "." { type hint; file "/etc/bind/db.root"; }; // be authoritative for the localhost forward and reverse zones, and for // broadcast zones as per RFC 1912 zone "localhost" { type master; file "/etc/bind/db.local"; }; zone "127.in-addr.arpa" { type master; file "/etc/bind/db.127"; }; zone "0.in-addr.arpa" { type master; file "/etc/bind/db.0"; }; zone "255.in-addr.arpa" { type master; file "/etc/bind/db.255"; }; };
Проверяем:
Перезапускаем bind
:
Пробуем с клиента А:
И лог BIND:
21-Mar-2018 16:15:30.585 client 10.0.2.145#55753 (bindtest.rtfm.co.ua): view network-A: query: bindtest.rtfm.co.ua IN A +E (10.0.1.157)
Пробуем с клиента из подсети В:
И лог BIND:
21-Mar-2018 16:15:30.585 client 10.0.2.145#55753 (bindtest.rtfm.co.ua): view network-A: query: bindtest.rtfm.co.ua IN A +E (10.0.1.157)
21-Mar-2018 16:16:12.364 client 10.0.3.164#36711 (bindtest.rtfm.co.ua): view network-B: query: bindtest.rtfm.co.ua IN A +E (10.0.1.157)
И пробуем из мира:
Лог:
21-Mar-2018 16:15:30.585 client 10.0.2.145#55753 (bindtest.rtfm.co.ua): view network-A: query: bindtest.rtfm.co.ua IN A +E (10.0.1.157)
21-Mar-2018 16:16:12.364 client 10.0.3.164#36711 (bindtest.rtfm.co.ua): view network-B: query: bindtest.rtfm.co.ua IN A +E (10.0.1.157)
21-Mar-2018 16:16:52.487 client 194.***.***.45#12659 (bindtest.rtfm.co.ua): view NotMatched: query: bindtest.rtfm.co.ua IN A +E (10.0.1.157)
Один нюанс тут — если мы вызов curl
с любого из клиентов — он нам везде вернёт 34.253.58.71:
А в логе будет указано сработвашее условие NotMached:
21-Mar-2018 16:36:14.348 client 52.213.251.132#13928 (bindtest.rtfm.co.ua): view NotMatched: query: bindtest.rtfm.co.ua IN A -EDC (10.0.1.157)
21-Mar-2018 16:36:14.390 client 34.245.138.44#57054 (bindtest.rtfm.co.ua): view NotMatched: query: bindtest.rtfm.co.ua IN AAAA -EDC (10.0.1.157)
Если обратить внимание на client — 52.213.251.132 и 34.245.138.44 — то становится понятна причина, вспомним наши ACL-ки:
... acl "subnet-a" { 10.0.2.0/24; }; acl "subnet-b" { 10.0.3.0/24; }; ...
Кроме того — всоминаем resolv.conf
, о котором упоминалось в начале — он содержит 10.0.0.2, который уже выполняет запрос через вышестоящий DNS, в результате к нашему ns1-example.rtfm.co.ua
запросы приходят не с IP Клиента А и В, а от внешних DNS-серверов.
Обновим /etc/resolv.conf
например на Клиенте А, укажем IP нашего DNS, что бы Клиент А обращался к нему напрямую:
nameserver 34.253.58.71
На BIND обновляем ACL для подсети-а:
... acl "subnet-a" { 10.0.2.0/24; 34.242.8.220; }; ...
Перезапускаем:
Пробуем curl
с клиента-А:
И лог BIND:
21-Mar-2018 16:44:39.276 client 34.242.8.220#39845 (bindtest.rtfm.co.ua): view network-A: query: bindtest.rtfm.co.ua IN AAAA + (10.0.1.157)
Вот — как-то так оно всё и работает…