ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

Let’s Encrypt wildcard через DNS-01: acme.sh, TSIG для BIND9 и сценарии с NSD без ручных правок

Практическая настройка DNS-01 для wildcard-сертификатов Let’s Encrypt с acme.sh: создание TSIG-ключей, update-policy в BIND9, проверка nsupdate/dig, учёт split-horizon и рабочие схемы для NSD (slave или делегирование).
Let’s Encrypt wildcard через DNS-01: acme.sh, TSIG для BIND9 и сценарии с NSD без ручных правок

Зачем DNS-01 для wildcard и чем он сложнее «обычного» ACME

Wildcard-сертификат (например, *.example.com) в Let’s Encrypt выпускается только через проверку DNS-01. В отличие от HTTP-01/TLS-ALPN-01, здесь вы доказываете владение доменом публикацией TXT-записи вида _acme-challenge.example.com со значением challenge-токена.

Плюсы DNS-01: не нужен доступ к веб-серверу, можно выпускать сертификаты для хостов, которые ещё не «живут» на этом сервере, и главное — поддержка wildcard. Минусы: нужно автоматизировать публикацию TXT, учитывать кеширование (TTL) и не попасть в ловушки split-horizon DNS.

Дальше — практический сценарий под админов: acme.sh как ACME-клиент, BIND9 с RFC2136 dynamic update через TSIG, и варианты, если у вас NSD (где RFC2136 нет).

Базовая схема выпуска wildcard через DNS-01

Процесс всегда одинаковый:

  1. acme.sh запрашивает сертификат для *.example.com (обычно вместе с example.com как SAN).
  2. ACME-сервер требует создать TXT на _acme-challenge.example.com.
  3. acme.sh добавляет TXT в авторитативный DNS (через RFC2136 или хук).
  4. acme.sh ждёт, пока запись станет видна снаружи, и подтверждает challenge.
  5. Сертификат выпущен: остаётся установить его и настроить обновление без простоя.

Самые частые проблемы: обновляете не тот сервер/не тот view, TXT виден только изнутри, или изменения не успевают разойтись по slave-серверам.

Схема DNS-01: публикация TXT _acme-challenge и проверка Let’s Encrypt

Подготовка DNS: TTL и split-horizon без сюрпризов

TTL для _acme-challenge

Если в зоне высокий TTL (например, 3600–86400), отладка и продления превращаются в ожидание и «фантомные» проверки. Практичный подход: задать для _acme-challenge TTL 60–300 секунд. Для самой зоны TTL можно оставить прежним.

Split-horizon DNS: почему «у меня видно, а Let’s Encrypt — нет»

Split-horizon — это когда домен резолвится по-разному из внутренней сети и из интернета. Для DNS-01 ключевое: TXT должен быть доступен публично с авторитативных NS, которые видит Let’s Encrypt.

Публикуйте TXT challenge именно в публичной DNS-плоскости. Внутренний view/зона могут быть отдельными, но challenge обязан попадать во внешнюю.

Если используете BIND9 с view, убедитесь, что динамическое обновление применяется к публичному view. Полезный разбор подходов и типовых ошибок — в статье про split-horizon views в BIND.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

BIND9: RFC2136 dynamic update + TSIG (надёжно и безопасно)

BIND9 поддерживает RFC2136 dynamic update. Для DNS-01 это один из самых предсказуемых способов: acme.sh добавляет/удаляет TXT, BIND проверяет запрос TSIG-ключом, изменения появляются сразу, без ручного редактирования файлов зон.

1) Создаём TSIG-ключ для обновлений

На DNS-сервере (master), где находится зона example.com:

sudo tsig-keygen -a hmac-sha256 acme-example > /etc/bind/keys/acme-example.key
sudo chmod 640 /etc/bind/keys/acme-example.key
sudo chown root:bind /etc/bind/keys/acme-example.key

В файле будет блок key и секрет. Секрет нужен и BIND, и acme.sh на хосте, который выполняет выпуск.

2) Ограничиваем права: только TXT и только _acme-challenge

Минимально безопасный вариант — разрешить изменения только для конкретного имени и типа через update-policy:

include "/etc/bind/keys/acme-example.key";

zone "example.com" {
  type master;
  file "/var/lib/bind/db.example.com";

  update-policy {
    grant acme-example name _acme-challenge.example.com. txt;
  };
};
  • Имя в политике указывайте как FQDN с точкой на конце.
  • Не выдавайте ключу права на всю зону без необходимости.
  • Файл зоны держите там, где BIND может писать сам (часто /var/lib/bind), иначе динамика будет «успешной», но фактически не сохранится.

3) Проверяем руками: nsupdate + dig

Перед автоматизацией стоит проверить всё вручную.

Добавляем тестовую TXT:

nsupdate -k /etc/bind/keys/acme-example.key << 'EOF'
server 127.0.0.1
zone example.com
update add _acme-challenge.example.com 60 TXT "test-token"
send
EOF

Проверяем на авторитативном сервере:

dig @127.0.0.1 _acme-challenge.example.com TXT +short

И отдельно проверяем публичную видимость, обращаясь к вашим авторитативным NS напрямую:

dig @ns1.example.com _acme-challenge.example.com TXT +short

Удаляем тест:

nsupdate -k /etc/bind/keys/acme-example.key << 'EOF'
server 127.0.0.1
zone example.com
update delete _acme-challenge.example.com TXT
send
EOF

4) Подключаем acme.sh по RFC2136 (dns_nsupdate)

acme.sh умеет RFC2136. Задаём параметры через переменные окружения:

export NSUPDATE_SERVER="ns1.example.com"
export NSUPDATE_KEY="acme-example"
export NSUPDATE_KEY_ALG="hmac-sha256"
export NSUPDATE_KEY_SECRET="BASE64SECRET=="

Выпускаем сертификат сразу на голый домен и wildcard:

acme.sh --issue --dns dns_nsupdate -d example.com -d '*.example.com'

Если у вас split-horizon и ns1 внутри/снаружи разный, в NSUPDATE_SERVER указывайте endpoint, который обновляет именно публичную зону.

Split-horizon в BIND9: как не обновлять «не тот» view

Когда зона определена в нескольких view, запрос dynamic update попадёт в тот view, куда BIND отнесёт клиента по match-clients. Из-за этого acme.sh может успешно обновлять внутренний view, а Let’s Encrypt будет проверять внешний и получать NXDOMAIN.

Практика, которая уменьшает число неожиданностей:

  • Сделайте отдельный адрес/интерфейс для обновлений публичной зоны и привяжите его к публичному view (через listen-on в нужном view).
  • В match-clients публичного view явно разрешите IP хоста, где запускается acme.sh.
  • Во внутреннем view обновления либо запретите, либо используйте другой ключ, чтобы не перепутать среды.

Для диагностики смотрите логи named по событиям update и сравнивайте ответы dig @внутренний-IP и dig @внешний-IP для _acme-challenge.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

NSD и DNS-01: почему «TSIG как в BIND» не работает и что делать

NSD — быстрый авторитативный сервер, но он принципиально не поддерживает RFC2136 dynamic update. Поэтому сценарий «acme.sh добавил TXT через nsupdate/TSIG прямо в NSD» в чистом виде не реализуется.

Рабочие схемы под DNS-01 обычно такие:

  1. NSD как slave, а master — BIND9 или Knot: acme.sh обновляет master по TSIG, NSD подтягивает изменения по AXFR/IXFR.
  2. Делегирование _acme-challenge.example.com на отдельный DNS, где есть RFC2136 или API.
  3. Хук + перезагрузка: скрипт правит include-файл/отдельную зону и вызывает nsd-control reload (работает, но требует аккуратной синхронизации и контроля прав).

Вариант A (предпочтительный): BIND9 master + NSD slave

Публичные NS могут быть NSD, но источник зоны — master. Тогда acme.sh обновляет master по TSIG, а NSD быстро подхватывает изменения через transfer.

Пример на master (BIND9):

include "/etc/bind/keys/acme-example.key";
include "/etc/bind/keys/xfr-nsd.key";

zone "example.com" {
  type master;
  file "/var/lib/bind/db.example.com";

  update-policy {
    grant acme-example name _acme-challenge.example.com. txt;
  };

  allow-transfer {
    key xfr-nsd;
  };
};

На NSD настраиваете slave-зону, master-адрес и TSIG для XFR. Синтаксис зависит от версии NSD, но суть одна: принимать transfer только от master и (по возможности) только с ключом.

Вариант B: делегировать _acme-challenge на отдельный DNS

Если не хотите менять архитектуру зоны, делегируйте поддомен _acme-challenge на отдельный DNS, который умеет динамику:

_acme-challenge 60 IN NS ns-acme1.example.net.
_acme-challenge 60 IN NS ns-acme2.example.net.

Дальше поднимаете отдельную зону _acme-challenge.example.com на выбранном DNS и обновляете её через RFC2136/TSIG. Плюс в том, что split-horizon основной зоны не мешает: challenge живёт отдельно и публично.

Split-horizon в BIND9: внутренний и внешний view и попадание dynamic update

Zero downtime renew: обновление сертификатов без разрыва соединений

Чтобы продление проходило без простоя, важны три вещи: стабильные пути файлов, «мягкая» перезагрузка сервиса и запуск хука только при реальном обновлении.

  • Кладите ключ и fullchain в фиксированные пути, которые использует веб-сервер.
  • Для Nginx обычно достаточно reload, он перечитает сертификаты без обрыва активных соединений.
  • Следите, чтобы пользователь, под которым работает acme.sh (cron), имел права писать файлы и выполнять reload-команду.

Пример установки сертификата и команды перезагрузки для Nginx:

acme.sh --install-cert -d example.com --key-file /etc/ssl/private/example.com.key --fullchain-file /etc/ssl/certs/example.com.fullchain.pem --reloadcmd "systemctl reload nginx"

Важно: домен в --install-cert должен совпадать с тем, под которым acme.sh хранит конфигурацию выпуска. Если вы выпускали сразу example.com и *.example.com, чаще всего это базовый домен example.com.

Частые ошибки и быстрая диагностика

TXT появился «у меня», но Let’s Encrypt его не видит

  • Проверяйте не резолвер, а авторитативные NS: dig @ns1.example.com _acme-challenge.example.com TXT.
  • При split-horizon убедитесь, что обновляете публичный view.
  • Если есть slave-серверы, проверьте, что они подтянули новую serial (и что transfer разрешён).

TSIG не проходит

  • Сверьте NSUPDATE_KEY, NSUPDATE_KEY_ALG и секрет.
  • Проверьте алгоритм: часто путают hmac-sha256 и hmac-sha512.
  • Имя ключа в BIND и у клиента должно совпадать посимвольно.

Параллельные выпуски ломают TXT

ACME может создавать несколько TXT для одного имени, если вы одновременно выпускаете несколько сертификатов. Убедитесь, что ваш механизм добавляет отдельные значения в RRset, а не перезатирает его одним значением. В связке acme.sh + RFC2136 обычно всё корректно; проблемы чаще появляются в самописных хуках.

Кеширование мешает тестам

Всегда отличайте «авторитативный ответ» от ответа рекурсивного резолвера. Для разбора используйте запросы к авторитативным серверам и при необходимости dig +trace. Низкий TTL именно для _acme-challenge заметно упрощает жизнь.

Чек-лист перед включением автопродления

  1. Публичные авторитативные NS отдают TXT _acme-challenge наружу.
  2. TTL для challenge — 60–300 секунд или другой осмысленный минимум.
  3. TSIG-ключ ограничен до конкретного имени и типа TXT через update-policy.
  4. Если участвует NSD: либо он slave и получает IXFR/AXFR, либо _acme-challenge делегирован отдельно.
  5. acme.sh запускается по расписанию и имеет доступ к секретам и командам reload.
  6. Вы проверили ручной прогон продления (без ожидания срока) и посмотрели логи.

Итоги

Для wildcard Let’s Encrypt через DNS-01 наиболее чистая схема — авторитативный DNS с RFC2136 и аккуратно ограниченный TSIG-ключ, который умеет менять только _acme-challenge. В BIND9 это делается прозрачно через update-policy, а acme.sh хорошо автоматизирует выпуск и продление.

Если у вас NSD, прямые dynamic updates недоступны, но задача всё равно решается: используйте master (BIND9/Knot) + NSD как slave или делегируйте _acme-challenge на отдельную зону. В результате вы получаете предсказуемый DNS update и renew без ручных правок и без простоя.

Поделиться статьей

Вам будет интересно

SSH hardening на VDS: Fail2ban vs sshguard, Match blocks и безопасные политики доступа OpenAI Статья написана AI (GPT 5)

SSH hardening на VDS: Fail2ban vs sshguard, Match blocks и безопасные политики доступа

Пошагово укрепляем SSH на VDS без лишней паранойи: готовим аварийный откат, переводим вход на ключи, запрещаем root и пароли, огра ...
Nginx: try_files, index и приоритет location — как избежать 404 и ловушек rewrite OpenAI Статья написана AI (GPT 5)

Nginx: try_files, index и приоритет location — как избежать 404 и ловушек rewrite

Разбираем, как Nginx выбирает location и что реально проверяет try_files, когда срабатывает index и где чаще всего появляются 404. ...
PostgreSQL: auto_explain, pg_stat_statements и простая APM-диагностика запросов OpenAI Статья написана AI (GPT 5)

PostgreSQL: auto_explain, pg_stat_statements и простая APM-диагностика запросов

Пошагово настраиваем pg_stat_statements и auto_explain для поиска медленных запросов без тяжёлых APM-систем. Разберём log_min_dura ...