14 октября 2016 г. 22:04
Написал soar

IPv6 PTR в BIND9

Зачем?

Когда передо мной встала задача разобраться в том, как быстро можно настроить обратный резолвинг IPv6-адресов в DNS, некому было подсказать мне правильный путь. Я несколько раз перечитывал документацию по BIND, пытаясь понять, действительно ли он не может сделать то, чего я хочу.

Забегая вперёд, я могу сказать, что ниже будет описан очень неправильный путь. И если вам нужно решить ту же проблему, что и мне, а именно настроить обратные записи для большого пула узлов - попробуйте сначала AllKnowingDNS. Скорее всего это то, что вам нужно. Единственная проблема этого сервера в том (если не считать проблемой Perl), что это отдельный сервер, и имея пул из N серверов BIND, вам придется запустить еще N серверов с AllKnowingDNS.

А ниже я просто расскажу о том, какие костыли можно под это всё подставить.

Немного RFC

RFC1912 из далекого 1996-го гласит:

Every Internet-reachable host should have a name. The consequences
of this are becoming more and more obvious. Many services available
on the Internet will not talk to you if you aren't correctly
registered in the DNS.

IPv6, как известно, появился в том же 1996ом, но, предполагаю, тогда еще никто не задумывался, что делать с его объемами адресов и как назначать им эти самые имена. Примерно через 10 лет появился RFC4472 - Operational Considerations and Issues with IPv6 DNS, в котором как раз разбираются вопросы связанные этим. Однако сейчас, спустя 20 лет, по-прежнему остаются нерешенными некоторые проблемы.

Как это было в IPv4

Как видно из заголовка - писать я буду про BIND. Он давно завоевал моё уважение, как вещь, которую невероятно тяжело поломать и которой можно доверить такую важную часть инфраструктуры как DNS.

Что мы имели в IPv4? Лично я разбивал все имеющиеся зоны на зоны /24, создавая для каждой отдельный файл и прописывая его в конфигурации сервера:

zone "2.0.192.in-addr.arpa" {
    type master;
    file "/etc/bind/rzones/2.0.192.in-addr.arpa.rzone";
};

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

$ORIGIN 2.0.192.in-addr.arpa.
$TTL    7200

@                   IN      SOA     ns.example.com. root.example.com. (
                                        1484147094          ; Serial
                                        3600                ; Refresh
                                        7200                ; Retry
                                        604800              ; Expire
                                        86400               ; Negative Cache TTL
)

                    NS      ns1.example.com.
                    NS      ns2.example.com.
                    NS      ns3.example.com.

1          PTR special-name.example.com.
2          PTR another-name.example.com.

$GENERATE 3-254        $ PTR host-$-2-0-192.example.com.

В этом примере мы сопоставляем два имени special-name.example.com. и another-name.example.com. в 192.0.2.1 и 192.0.2.2 соответственно, а для всех остальных генерируем имена по шаблону host-3-2-0-192.example.com., host-4-2-0-192.example.com., и т.д. Просто? Очень.

Что делать с IPv6?

Механизм $GENERATE в BIND принимает всего один аргумент являющийся одним диапазоном и проходит по нему один цикл. Более того, результаты своей работы он сохраняет в памяти и отдает их оттуда, а не генерирует на лету. Поэтому если вы, допустим, владелец /32 зоны, можно представить, сколько памяти понадобилось бы на то, чтобы покрыть все адреса. Получается, что дробить зону уже нет смысла и мы можем написать просто один файл:

zone "8.b.d.0.1.0.0.2.ip6.arpa" {
    type master;
    file "/etc/bind/rzones/8.b.d.0.1.0.0.2.ip6.arpa.rzone";
};

Но что делать с файлом зоны? Как его генерировать? Да, у BIND есть еще wildcard-имена (*). Однако звёздочка действует от точки до точки, т.е. строка a.b.c не попадёт под шаблон *.

Сказать, что на поиск решения я потратил не один час - не сказать ничего. Конечная конфигурация очень странная и я до сих пор не понимаю, как это работает. Поэтому если кто-то разобрался в исходниках - я буду рад услышать ответ.

$ORIGIN 8.b.d.0.1.0.0.2.ip6.arpa.
$TTL    7200

@                   IN      SOA     ns.example.com. root.example.com. (
                                        1484147094          ; Serial
                                        3600                ; Refresh
                                        7200                ; Retry
                                        604800              ; Expire
                                        86400               ; Negative Cache TTL
)

                    NS      ns1.example.com.
                    NS      ns2.example.com.
                    NS      ns3.example.com.
                    NS      ns4.example.com.
                    NS      ns5.example.com.

0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.1.0.0.0.0.0.0.0  PTR special-name.example.com.
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.2.0.0.0.0.0.0.0  PTR another-name.example.com.

* PTR common-ipv6-ptr.example.com.
*.0 PTR common-ipv6-ptr.example.com.
*.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.
*.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0 PTR common-ipv6-ptr.example.com.

Да, именно так. Именно такой набор звёздочек и нулей и никак иначе. Я пробовал комбинировать несколько звёздочек и пробовал уместить это в одну строку - не работает никак по-другому.

И всё бы казалось хорошо, но:

soar@localhost:~$ nslookup -type=PTR 2001:db8:0:0:f00::
0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa        name = common-ipv6-ptr.example.com
soar@localhost:~$ nslookup -type=PTR 2001:db8:0:0:f000::
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa        name = common-ipv6-ptr.example.com
soar@localhost:~$ nslookup -type=PTR 2001:db8:0:f::
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa        name = common-ipv6-ptr.example.com
soar@localhost:~$ nslookup -type=PTR 2001:db8:0:f0::
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa        name = common-ipv6-ptr.example.com
soar@localhost:~$ nslookup -type=PTR 2001:db8:0:f00::
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa        name = common-ipv6-ptr.example.com
soar@localhost:~$ nslookup -type=PTR 2001:db8:0:f000::
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.0.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa        Non-existent domain
soar@localhost:~$ nslookup -type=PTR 2001:db8:f::
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.0.0.0.8.b.d.0.1.0.0.2.ip6.arpa        name = common-ipv6-ptr.example.com
soar@localhost:~$ nslookup -type=PTR 2001:db8:f0::
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.0.0.8.b.d.0.1.0.0.2.ip6.arpa        name = common-ipv6-ptr.example.com
soar@localhost:~$ nslookup -type=PTR 2001:db8:f00::
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.0.8.b.d.0.1.0.0.2.ip6.arpa        name = common-ipv6-ptr.example.com
soar@localhost:~$ nslookup -type=PTR 2001:db8:f000::
0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.f.8.b.d.0.1.0.0.2.ip6.arpa        name = common-ipv6-ptr.example.com

Если протестировать все возможные адреса, то проблема выглядит так: как только в адресе появляется 49-ый бит (напр. 2001:db8:0:8000::) и до тех пор пока не появится 48-ой (напр. 2001:db8:1::) - сервер эти адреса резолвить отказывается, независимо от любых других битов. С чем связано такое поведение я так и не выяснил. И это поведение выглядит еще более странным чем то, что эта конфигурация вообще работает.

Заключение

Фича ли это? Однозначно. Стоит ли её использовать? Только если у вас большая зона, вы только внедряете IPv6 и вам необходимо решить эту проблему здесь и сейчас, не запуская дополнительных сервисов. Во всех остальных случаях, особенно если хотите решить вопрос правильно - используйте AllKnowingDNS.

Комментарии