С 14:00 UTC 11 октября 2026 года корневая зона DNS подписывает свой набор ключей только новым ключом KSK-2024 (key tag 38696). Старый KSK-2017 (20326) ещё лежит в зоне, но подписей им больше нет. Джефф Хастон из APNIC по своим замерам пишет, что смена прошла без заметного сбоя. За четыре дня до неё Эфраин Гарай проверил пакеты и образы и среди прочего показал, что BIND 9.16.50 из Debian 11, в поставке которого есть только старый ключ, при первом запуске сам начинает доверять новому.
Через 20 дней после смены я запустил тот же BIND с нуля, с пустым каталогом состояния, как запускается пересозданный контейнер. Он отвечает SERVFAIL на всё, включая неподписанный google.com. Для unbound новый якорь при таком старте получает unbound-anchor из Debian 12, если ему открыт HTTPS к data.iana.org. Если HTTPS закрыт, он пишет файл со старым ключом и выходит с кодом 0. dnsmasq из Debian 12 со своим файлом якорей отказывает на всём, а с якорями, где есть 38696, работает. Такие якоря ему подставляет сервисная обёртка дистрибутива, а контейнер с голым dnsmasq её обходит.
Что изменилось 11 октября
У валидирующего резолвера есть якорь доверия: ключ корня (или его хеш DS), с которого начинается проверка любой цепочки подписей. В корне сейчас опубликованы два ключа подписи ключей и один ключ подписи зоны. Набор DNSKEY подписан только 38696. Это видно одним запросом к живому корню, я сохранил вывод 30 октября:
DNSKEY flags=257 tag=38696DNSKEY flags=256 tag=8763DNSKEY flags=257 tag=20326RRSIG(DNSKEY) covered=dnskey keytag=38696
Резолвер, который знает только 20326, получает оба ключа, но подпись набора проверить не может. Поэтому первый пункт чек-листа BI.ZONE на Хабре, «в dig DNSKEY . должен быть 38696», сам по себе мало что говорит: с флагом +cd или через невалидирующий пересыльщик этот набор придёт и тому, кто 38696 не доверяет. Надёжнее следующий пункт того же чек-листа: флаг ad в ответе на подписанное имя.
Автообновление якорей по RFC 5011 работает у резолвера, который хранит состояние на диске и прожил с новым ключом в наборе не меньше 30 дней. Сам RFC прямо пишет, что начальную настройку он не решает. Контейнер без volume при каждом пересоздании начинает с якоря из образа, и всё решается тем, что лежит в этом образе.
Какие выпуски и образы несут только старый ключ, подробно разобрал Гарай: 21 выпуск дистрибутивов, 11 образов, прошивки OpenWrt, плюс вахта из десяти резолверов через саму смену. Повторять его таблицу я не стал. Но все его запуски «с нуля» сделаны 7 октября, когда набор DNSKEY ещё был подписан 20326. Пути самовосстановления после смены он сам отметил как неизмеренные. Их я и проверил.
Как запускал
Пакеты брал из архивов выпусков (apt-get download, для Debian 11 — archive.debian.org), распаковывал dpkg-deb -x без установки и запускал бинарники в одноразовых контейнерах debian:11 и debian:12 на непривилегированных портах. Каталог состояния каждый раз пустой. Три имени: iana.org (подписан), dnssec-failed.org (подпись намеренно испорчена, правильный ответ — SERVFAIL) и google.com (не подписан). Все прогоны — 31 октября между 10:38 и 10:40 UTC. Для каждого отказа рядом стоит контроль: тот же бинарник с якорем, где есть 38696.
BIND со старым якорем больше не принимает новый ключ при старте
Гарай объяснил, почему 7 октября BIND с одним старым ключом работал: пустое хранилище ключей при инициализации проверяет набор DNSKEY поставляемым ключом и записывает актуальные. Пока набор подписывал 20326, проверка проходила, и в хранилище попадали оба ключа.
Сейчас тот же пакет bind9 9.16.50 с штатным bind.keys:
== прогон shipped, пустой каталог состояния iana.org SERVFAIL ad=нет dnssec-failed.org SERVFAIL ad=нет google.com SERVFAIL ad=нет-- named.log: 13 validating ./DNSKEY: no valid signature found (DS) 1 no valid RRSIG resolving './DNSKEY/IN': 198.41.0.4#53
Строки no valid RRSIG пришли как минимум от пяти разных корневых серверов. Тот же named, которому я подложил bind.keys с двумя якорями, 20326 и 38696 (те же два ключа есть в bind.keys из Debian 12):
iana.org NOERROR ad=да dnssec-failed.org SERVFAIL ad=нет google.com NOERROR ad=нетmanaged-keys-zone: ... DNSKEY ID 38696 is now trusted, waiving the normal 30-day waiting period.
Механизм тот же, но исход противоположный, потому что сменилась подпись в корне. Результат «BIND из Debian 11 доверяет новому ключу» был верен 7 октября и неверен для контейнера, пересозданного после 11-го. Неподписанный google.com тоже не отвечает: чтобы доказать, что у имени нет подписи, резолвер должен пройти цепочку от корня, а она обрывается на первом шаге.
unbound-anchor возвращает 0, даже когда не обновил якорь
В Fedora, RHEL и Amazon Linux, как отмечает Гарай, перед unbound запускается unbound-anchor. Встроенный якорь в версии 1.17.1 из Debian 12 — только 20326. Если проверка по DNS не проходит, программа скачивает root-anchors.xml с data.iana.org и проверяет его подпись встроенным сертификатом ICANN Root CA (действует до 18 декабря 2029 года).
С открытым HTTPS всё работает:
== якоря нет, HTTPS к data.iana.org открытunbound-anchor rc=1root.key: DS 20326 DS 38696
По документации код 1 значит «якорь обновлён». Unbound с таким файлом отвечает на iana.org с флагом ad и отказывает на dnssec-failed.org.
Потом я указал программе вместо сервера IANA адрес 127.0.0.1, где никто не слушает:
== якоря нет, data.iana.org недоступенunbound-anchor rc=0connect: Connection refusedcould not fetch root-anchors/root-anchors.xmlroot.key: DS 20326
Файл записан со старым ключом, код выхода 0. Unbound с этим файлом пишет failed to prime trust anchor -- DNSKEY rrset is not secure и отдаёт SERVFAIL на все три имени. В контейнере, где наружу открыт только DNS, а HTTPS закрыт, скрипт запуска не узнает, что якорь не обновился: код 0 не значит, что якорь рабочий. Недоступность я изобразил подменой адреса. Файрвол с таймаутом по логике программы должен дать тот же код, но отдельно я это не проверял.
dnsmasq: новый ключ есть в дистрибутиве, но не в файле dnsmasq
В Debian 12 пакет dnsmasq-base 2.90 несёт trust-anchors.conf с одной строкой trust-anchor=.,20326. Голый dnsmasq с ним, апстримы 1.1.1.1 и 8.8.8.8:
iana.org SERVFAIL ad=нет dnssec-failed.org SERVFAIL ad=нет google.com SERVFAIL ad=нет 2 reply . is BOGUS DNSKEY
Сервисная обёртка пакета dnsmasq (/usr/share/dnsmasq/init-system-common) якоря из trust-anchors.conf не берёт. Она читает /usr/share/dns/root.ds из пакета dns-root-data, где 38696 уже есть, и превращает каждую строку в --trust-anchor. Я прогнал ровно её sed и запустил тот же бинарник с этими якорями: iana.org — NOERROR с ad, dnssec-failed.org — SERVFAIL. Дистрибутив обновил якорь в соседнем пакете и в обёртке, а контейнер с ENTRYPOINT ["dnsmasq", …] эту обёртку обходит.
С unbound в Debian 12, похоже, то же самое. В таблице Гарая он работает, а сервис Debian перед запуском копирует root.key из dns-root-data (это отмечает и сам Гарай). Встроенный якорь unbound-anchor той же версии, как показано выше, содержит только 20326. Контейнер, который запускает unbound без этой копии и без явно прописанного 38696, зависит от unbound-anchor и открытого HTTPS.
Сводка
|
Запуск с нуля, 31.10 |
Якорь |
iana.org |
dnssec-failed.org |
google.com |
|---|---|---|---|---|
|
BIND 9.16.50 (Debian 11), штатный |
20326 |
SERVFAIL |
SERVFAIL |
SERVFAIL |
|
тот же |
20326 + 38696 |
NOERROR, ad |
SERVFAIL |
NOERROR |
|
|
скачал 38696, rc=1 |
NOERROR, ad |
SERVFAIL |
NOERROR |
|
|
20326, rc=0 |
SERVFAIL |
SERVFAIL |
SERVFAIL |
|
dnsmasq 2.90 (Debian 12), |
20326 |
SERVFAIL |
SERVFAIL |
SERVFAIL |
|
тот же dnsmasq, якоря как у сервисной обёртки |
20326 + 38696 |
NOERROR, ad |
SERVFAIL |
NOERROR |
Во всех трёх случаях исправление есть в дистрибутиве, но лежит не в той программе, которую запускает типичный контейнер: в соседнем пакете, в обёртке сервиса или на HTTPS-сервере IANA. Пока корень подписывался старым ключом, разницы не было видно.
Как проверить свой резолвер
Смотреть на версию пакета или на dig DNSKEY . бесполезно, проверять надо ответы:
dig @<ваш резолвер> +dnssec iana.org A | grep -E 'status|flags'dig @<ваш резолвер> dnssec-failed.org A | grep status
Рабочий валидирующий резолвер отвечает на iana.org NOERROR с флагом ad и SERVFAIL на dnssec-failed.org. В моих прогонах якорь только с 20326 выглядел одинаково: SERVFAIL на оба имени и на неподписанное, а в логе резолвера — ошибка проверки ./DNSKEY. Делать это стоит после пересоздания контейнера с пустым состоянием: долгоживущий процесс может держать ключ в памяти или в своём файле. Если у вас unbound-anchor в entrypoint, ищите 38696 в получившемся root.key: код 0 не гарантирует, что якорь обновлён. В своих Dockerfile для dnsmasq и unbound якорь с 38696 лучше задать явно, например из свежего dns-root-data.
Чего я не проверял
Резолвер с записываемым состоянием, проживший с 2025 года, мог принять 38696 по RFC 5011, и мои результаты к нему не относятся. Ubuntu, Alpine и сторонние образы вроде mvance/unbound я не перезапускал. Почему Pi-hole v5 из вахты Гарая с одним 20326 работал до 12 октября, мои прогоны не объясняют.
Версии: bind9 1:9.16.50 (Debian 11, archive.debian.org), unbound и unbound-anchor 1.17.1 (Debian 12), dnsmasq-base 2.90-4~deb12u2, dns-root-data 2024071801~deb12u1, dig из тех же выпусков.
ссылка на оригинал статьи https://habr.com/ru/articles/1102184/