Зачем DNS-01 и почему RFC2136 удобен для wildcard
Wildcard-сертификат (например, *.example.com) почти всегда означает проверку владения доменом через ACME DNS-01: CA ждёт, что в DNS появится специальная TXT-запись в _acme-challenge. HTTP-01 для wildcard не подходит, а DNS-01 подтверждает контроль над зоной на уровне DNS.
Если authoritative DNS у вас свой (BIND, hidden-master, собственная NS-инфраструктура) и вы не хотите зависеть от облачных API, самый прямой путь автоматизации — RFC2136 dynamic updates. ACME-клиент (например, lego) добавляет и удаляет TXT через аутентифицированный update (обычно TSIG), после чего ожидает, когда изменения станут видны тем NS, куда придёт проверка CA.
В этой схеме важны две вещи: безопасность (минимальные права в update-policy) и предсказуемый тайминг (понимание, где именно «застревает» propagation).
Как выглядит DNS-01 challenge на уровне записей
Для домена example.com ACME управляет записью:
_acme-challenge.example.com. TXT "<token>"
Wildcard не означает «запись на каждое имя». Challenge живёт в одной точке: _acme-challenge.example.com. Если вы заказываете сертификат сразу на example.com и *.example.com, клиент может создать один или несколько токенов (часто это несколько TXT на одном имени).
Если вы только планируете автоматизацию wildcard-сертификатов, полезно держать в голове ограничения DNS-01, гонки при параллельных запросах и типовые тайминги. В отдельной заметке я собрал практику: как автоматизировать wildcard через DNS-01.

Предварительные условия: authoritative DNS и где CA будет проверять
Перед настройкой RFC2136 убедитесь, что вы управляете именно authoritative DNS для зоны, а не только рекурсивным резолвером. Минимально рабочая картина такая:
- BIND является master (или hidden master) для
example.comи принимает dynamic updates. - Если есть secondary NS, они забирают изменения через IXFR/AXFR (это напрямую влияет на задержки).
- CA может прийти на любой NS из делегирования зоны, а не на тот сервер, который вы тестируете «по привычке».
Если зона обслуживается у внешнего DNS-провайдера и вы не можете включить RFC2136 на authoritative стороне, этот сценарий не применим: придётся использовать API провайдера или выносить challenge в отдельную зону, где у вас есть управление.
TSIG для RFC2136: создаём ключ и подключаем в named
RFC2136 без TSIG почти всегда заканчивается небезопасной конфигурацией. Делайте обновления только с TSIG и храните секрет как обычный production-секрет (минимум доступов, ротация при подозрении на компрометацию). Для современного BIND выбирайте hmac-sha256, если нет требований совместимости.
tsig-keygen -a hmac-sha256 acme-rfc2136 > /etc/bind/keys/acme-rfc2136.key
Содержимое файла будет выглядеть примерно так:
key "acme-rfc2136" {
algorithm hmac-sha256;
secret "BASE64SECRET==";
};
Подключите ключ в конфигурацию named (удобнее через отдельный файл и include):
include "/etc/bind/keys/acme-rfc2136.key";
Ограничьте права на файл ключа так, чтобы его мог читать только пользователь/группа BIND:
chown root:bind /etc/bind/keys/acme-rfc2136.key
chmod 640 /etc/bind/keys/acme-rfc2136.key
BIND update-policy: минимальные привилегии для _acme-challenge
Самая распространённая ошибка — разрешить ключу обновлять всю зону. Это превращает утечку TSIG в полный захват DNS. Для ACME обычно достаточно разрешить изменения только TXT в строго заданной точке _acme-challenge.
Пример для зоны example.com:
zone "example.com" {
type master;
file "/var/lib/bind/zones/db.example.com";
update-policy {
grant acme-rfc2136 name _acme-challenge.example.com. txt;
};
};
Что важно проверить в этом фрагменте:
- Права выданы на конкретное имя и тип: только
txtи только_acme-challenge.example.com. - Точка в конце FQDN обязательна, чтобы не получить относительное имя и неожиданные совпадения.
- Если зон несколько, безопаснее иметь отдельный TSIG на зону или, как минимум, отдельные
grantпод каждую зону.
Динамическая зона и файлы .jnl: что считается нормой
После включения dynamic updates BIND обычно создаёт рядом с файлом зоны журналы (например, .jnl). Это нормально: изменения фиксируются в журнале и затем применяются к зоне. Не редактируйте файл зоны вручную при активных обновлениях: легко получить рассинхрон и откаты. Для ручных правок используйте управляемый процесс (freeze/thaw) или вносите изменения через nsupdate.
Проверяем RFC2136 вручную через nsupdate (до интеграции с ACME)
До того как подключать lego или любой другой ACME-клиент, отладьте базовую цепочку: TSIG корректен, правило update-policy подходит, BIND принимает обновление именно в нужную зону/view.
Запустите nsupdate с вашим ключом:
nsupdate -k /etc/bind/keys/acme-rfc2136.key
Далее в интерактивном режиме:
server ns1.example.com
zone example.com
update add _acme-challenge.example.com. 60 TXT "test-token"
send
Проверьте authoritative-ответ прямо на NS:
dig @ns1.example.com _acme-challenge.example.com TXT +noall +answer
После проверки удалите тестовую запись:
nsupdate -k /etc/bind/keys/acme-rfc2136.key
server ns1.example.com
zone example.com
update delete _acme-challenge.example.com. TXT
send
Настройка lego для RFC2136: что учесть для wildcard
На практике lego часто используют в CI/CD, контейнерах и systemd-таймерах. Общая логика для RFC2136 всегда одна: lego должен уметь подписывать dynamic update вашим TSIG и отправлять его на authoritative DNS (обычно master или hidden master).
Обычно требуется указать такие параметры (конкретные имена переменных зависят от способа запуска и версии):
- DNS-сервер, куда отправлять update (желательно master/hidden master);
- имя TSIG-ключа;
- алгоритм TSIG;
- секрет TSIG;
- время ожидания перед проверкой (delay/timeout для propagation).
Практический подход: update отправляйте на master, а проверку TXT делайте по всем NS из делегирования — так вы заранее увидите, что CA тоже увидит запись.
Propagation: почему CA «не видит» TXT, хотя запись уже добавлена
Propagation в DNS-01 — это не только «подождать TTL». На wildcard через RFC2136 чаще всего встречаются три вида задержек:
- задержка репликации master → secondary (IXFR/NOTIFY/ACL/TSIG между NS);
- кэширование у резолверов (включая негативный кэш после NXDOMAIN);
- проверка CA у другого NS, чем тот, на котором вы «увидели» запись.
Для успешной валидации важно не то, что TXT виден «у вас», а то, что TXT виден на authoritative NS из делегирования зоны, потому что CA пойдёт именно туда.
Мини-чек диагностики: где именно тормозит
1) Проверка master:
dig @ns1.example.com _acme-challenge.example.com TXT +noall +answer
2) Проверка каждого NS из делегирования (особенно secondary):
dig @ns2.example.com _acme-challenge.example.com TXT +noall +answer
3) Проверка SOA serial на secondary (быстрый признак, что зона обновилась):
dig @ns2.example.com example.com SOA +noall +answer
Если serial на secondary не меняется, проблема обычно не в кэше, а в репликации (transfer/notify/ACL). Если же serial обновился, но где-то TXT не виден, смотрите views/split-horizon и то, какой view отвечает на публичные запросы. Если вы используете views, пригодится разбор типовых ошибок: как настроить BIND views (split-horizon) без сюрпризов.
TTL и negative caching: почему 60 секунд не всегда 60
Низкий TTL (например, 60) — хорошая практика для challenge-записей. Но если до добавления TXT резолвер успел получить NXDOMAIN на _acme-challenge, он мог закэшировать отрицательный ответ на время, зависящее от SOA (negative TTL). В итоге вы уже добавили TXT на authoritative, а часть резолверов ещё «уверена», что записи нет.
Поэтому при отладке проверяйте authoritative напрямую (через dig @nsX), а в автоматизации закладывайте адекватный delay ожидания propagation.
Типовые ошибки update-policy и как их быстро распознать
REFUSED при nsupdate/lego
Чаще всего это одна из причин:
- не совпали имя/секрет/алгоритм TSIG;
- в
update-policyнет правила, которое подходит именно под обновляемое имя и тип; - update попадает в другой view (split-horizon), где нет нужного
update-policy.
Смотрите логи named и для начала упрощайте тест: выполняйте update локально и явно задавайте server в nsupdate.
TXT появился на master, но не дошёл до secondary
Это почти всегда проблема transfer/notify. Проверьте:
- что secondary действительно настроен как slave для зоны и знает master;
- что master разрешает AXFR/IXFR этому secondary (ACL не режет transfer);
- что
notifyне отключён (по умолчанию включён) и secondary доступен по сети.
Если у вас несколько площадок, закладывайте propagation по худшему NS (часто это самый «дальний» secondary).
lego добавляет TXT, но ACME-проверка падает
Типичный сценарий: слишком короткое ожидание propagation и проверка только одного NS при диагностике. Увеличьте delay/timeout и проверяйте TXT на всех NS из делегирования. Ещё нюанс: некоторые клиенты создают несколько TXT на одном имени. Это нормально, BIND поддерживает несколько TXT, но в своих скриптах не делайте «грубую» чистку всех TXT в _acme-challenge, если у вас возможны параллельные запросы.

Эксплуатация: параллельные renew, чистка TXT и безопасность
В продакшене чаще ломается не первая выдача, а автоматизация и параллельность. Что обычно помогает:
- Один пайплайн обновления сертификатов на зону/имя за раз (избегайте гонок на создании/удалении TXT).
- TSIG-ключу выдавайте минимально необходимые права: только
_acme-challengeи толькоtxt. - Храните секреты отдельно от репозиториев и ротуйте ключи при любых подозрениях.
- Ориентируйтесь на худший по скорости secondary при выборе delay/timeout.
Если у вас BIND и ACME-автоматизация крутятся на отдельной машине, часто удобнее вынести это на отдельный сервер. Под такую задачу обычно берут VDS, чтобы держать DNS, мониторинг и обновление сертификатов в одном месте и контролировать сетевой доступ к RFC2136.
Чек-лист быстрой диагностики DNS-01 (RFC2136 + BIND)
- TXT появился на master при запросе напрямую?
- TXT появился на каждом NS из делегирования?
- SOA serial на secondary обновился?
- Нет ли влияния negative caching (особенно если раньше было NXDOMAIN)?
update-policyограничивает только нужное имя и тип?- Нет ли views/split-horizon, из-за которых update и публичные ответы идут в разные view?
Итог
ACME DNS-01 для wildcard через RFC2136 и BIND отлично работает, если соблюдены два условия: строго ограниченный доступ через TSIG и update-policy (только _acme-challenge и только txt), а также понимание propagation как цепочки master → secondary → authoritative NS, куда реально придёт CA.
Отладьте базу через nsupdate и проверки dig @nsX по всем NS, и тогда интеграция lego обычно становится финальным техническим шагом, а не лотереей на таймингах.


