Поддомены размножаются тихо и быстро: dev, stage, git, wiki, grafana, почтовый веб-интерфейс. Выпускать сертификат на каждое имя отдельно - значит вести список, который вечно отстаёт от жизни и рвётся в самый неудобный месяц. Wildcard закрывает вопрос одним файлом на *.example.com: сегодня он покрывает десяток имён, завтра появится ещё пять, и переоформлять ничего не придётся. Но за широту центр сертификации требует доказательства контроля над всей зоной сразу, и привычная проверка по HTTP здесь не работает. Ниже - механизм проверки DNS, токен Cloudflare, выпуск и автопродление wildcard, плагины для других провайдеров и лечение частых ошибок.

Чем wildcard сертификат отличается от сертификата на один домен

Обычный сертификат перечисляет конкретные имена в поле Subject Alternative Name: example.com, www.example.com, mail.example.com. Wildcard вместо перечисления держит звёздочку: *.example.com. Одна строка покрывает любые имена первого уровня - staging.example.com, api.example.com, pay.example.com.

У звёздочки два свойства, о которые спотыкаются при первом знакомстве. Первое: она не покрывает корень. Сертификат .example.com на адресе example.com браузер сочтёт недействительным, поэтому wildcard выпускают сразу парой: корневое имя и звёздочное в одном сертификате. Второе: звёздочка действует на один уровень. Имя a.b.example.com под .example.com не подпадает, для такого уровня нужна своя звёздочка *.b.example.com.

В конфигурации nginx это выглядит привычно:

server {
    listen 443 ssl;
    server_name example.com *.example.com;
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}

Срок жизни у wildcard такой же, как у любого сертификата Let's Encrypt, - 90 дней. Проверка доменная: центр убеждается в праве на имя, без бумаг о компании. Продление держит та же автоматика Certbot, что и у обычных сертификатов.

Звёздочку можно выпустить и на подуровень: запись вида *.staging.example.com закроет все стендовые имена разом. И про экономику: новых сертификатов на зарегистрированный домен выдаётся не больше пятидесяти в неделю, а wildcard вместо пачки одиночных выпусков тратит ровно один. Один файл в live, одна строка в конфигурации сервера, одно продление в расписании.

Механизм DNS проверки через TXT записи и требования протокола ACME

Проверка DNS-01 устроена как одноразовый пароль. Центр сертификации выдаёт клиенту токен, клиент вычисляет из токена и ключа своей учётной записи контрольное значение и публикует его в TXT записи с именем _acme-challenge.example.com. Центр опрашивает публичный DNS, сверяет значение и при совпадении подписывает сертификат. Открытый порт на веб-сервере для всего этого не нужен.

Для звёздочных имён такая проверка обязательна: протокол ACME не определяет HTTP проверку для wildcard, и выпустить звёздочный сертификат через неё нельзя. DNS проверка - единственный путь к wildcard через Let's Encrypt.

Побочные выгоды неочевидны, но приятны. Проверка работает для серверов за NAT, без белого адреса, для имён, у которых нет публичного сайта. Она избавляет от раскладывания проверочного файла по двадцати машинам за балансировщиком. Именно поэтому wildcard для внутренних стендов и служебных панелей почти всегда выпускают через DNS.

Два технических нюанса делают проверку гибче. Первый: имя _acme-challenge можно делегировать. Стандарты DNS разрешают указать для него CNAME или NS в другую зону, например в маленький служебный домен, который обновляется мгновенно и живёт отдельно от основного. Так поступают, когда основной DNS медленный или когда проверочные записи всех доменов компании хочется держать в одном месте. Второй нюанс: записей на одном имени может быть несколько. При одновременной проверке корня и звёздочного имени значения ложатся на одно и то же _acme-challenge, и это штатно. Старые проверочные записи имеет смысл подчищать: если ответ DNS разрастается, центр начинает его отклонять.

Создание ограниченного API токена Cloudflare для Certbot

Плагин будет править зону вместо человека, поэтому доступ выдают по принципу минимальных прав. У Cloudflare есть глобальный ключ Global API Key, который открывает всю панель и все домены учётной записи: утечка такого ключа равносильна передаче управления аккаунтом. Ограниченный токен API - правильная альтернатива: конкретные права на конкретные зоны и ничего сверх.

Certbot хватает одной строки прав: Zone, DNS, Edit - на те зоны, для которых выпускаются сертификаты. Токен создаётся в панели Cloudflare в разделе API Tokens личного профиля и сразу уходит в файл с секретом:

# ~/.secrets/certbot/cloudflare.ini
dns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567

Файл закрывают строгими правами:

chmod 600 ~/.secrets/certbot/cloudflare.ini

Certbot читает секрет при каждом выпуске и продлении и печатает предупреждение "Unsafe permissions on credentials configuration file", если права шире нужных. Предупреждение не отключается и повторяется при каждом продлении, лечится только корректным chmod. Для работы с токенами плагину нужен модуль cloudflare для Python не ниже версии 2.3.1; штатная установка подтянет его сама.

Установка плагина Cloudflare и выпуск wildcard сертификата

Плагин certbot-dns-cloudflare развивается в одном ритме с Certbot: актуальная ветка 5.8.0. В Debian и Ubuntu он ставится пакетом, в других системах - через pipx или как snap-пакет:

sudo apt install python3-certbot-dns-cloudflare

Полная последовательность от пустого сервера до готового wildcard умещается в пять шагов:

  1. установить Certbot и пакет плагина dns-cloudflare;
  2. создать токен Cloudflare с правами DNS Edit на нужные зоны;
  3. записать токен в cloudflare.ini и закрыть файл правами 600;
  4. запустить certonly с корневым и звёздочным именами в кавычках;
  5. проверить файлы в live и прописать их в конфигурацию веб-сервера.

Сам выпуск:

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \
  -d example.com \
  -d '*.example.com'

Звёздочное имя берут в одинарные кавычки, иначе оболочка попробует раскрыть звёздочку по файлам текущего каталога. Плагин сам создаст TXT запись, подождёт распространения и позовёт центр проверять; ожидание по умолчанию короткое, 10 секунд. Если зона обновляется неторопливо, паузу продлевают отдельным флагом:

sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \
  --dns-cloudflare-propagation-seconds 60 \
  -d example.com -d '*.example.com'

Результат окажется в привычном месте: /etc/letsencrypt/live/example.com/ с fullchain.pem и privkey.pem, как у любого сертификата Certbot. После этого конфигурация веб-сервера получает обычные пути, а звёздочное имя в server_name подхватит все поддомены сразу.

Автоматическое продление wildcard сертификата без ручных действий

Продление wildcard не требует новых команд: плагин, путь к секрету и оба имени Certbot уже записал в конфигурацию продления. Команда certbot renew создаст свежую TXT запись, дождётся проверки, удалит запись за собой и переключит ссылки в live на новую версию. Продление стартует, когда у сертификата прошла половина срока жизни.

Автоматику вешают на таймер systemd или cron, как для любых сертификатов:

17 3 * * * root certbot -q renew

Веб-сервер новые файлы сам не подхватит: он держит ключи в памяти с момента старта. Поэтому продлению добавляют хук перезагрузки:

sudo certbot renew --deploy-hook "systemctl reload nginx"

Хук сохранится в конфигурации продления и сработает при каждом успешном обновлении. Отрепетировать всю цепочку заранее позволяет сухой прогон: он выполняет и создание TXT записи, и удаление, но против тестового центра и без записи на диск:

sudo certbot renew --dry-run

Посмотреть сроки и пути помогает команда с наглядным списком:

sudo certbot certificates

В списке будут оба имени, дата окончания и пути к файлам. Письма об истечении срока центр шлёт на адрес учётной записи, и отписываться от них не стоит: это последний рубеж контроля, если токен или зона внезапно откажут.

Про секрет стоит помнить одну вещь: он живёт дольше сертификата. Перевыпуск токена в панели не ломает продление, если новое значение сразу положить в тот же cloudflare.ini. А вот перенос домена с Cloudflare на другой DNS ломает продление мгновенно, и без наблюдения об этом узнают из писем об истечении срока или из красной страницы браузера.

Другие провайдеры DNS и выпуск wildcard на собственном сервере

Cloudflare - популярный, но не единственный вариант. В официальном списке Certbot есть плагины для DigitalOcean, Google, Linode, DNSimple, DNS Made Easy, NSOne и других провайдеров, а таблица сторонних плагинов покрывает десятки сервисов: Amazon Route 53, Hetzner Cloud, региональные DNS-хостинги. Устройство у всех одинаковое: файл с ключами, флаг --credentials, флаг --propagation-seconds; различия только в имени пакета и формате секрета.

Для собственной зоны BIND есть плагин rfc2136: он обновляет зону динамически по TSIG-ключу, и внешний API не нужен вовсе. Внутренний стенд получает wildcard полностью локально: DNS, центр сертификации и веб-сервер на одном железе, без выхода наружу.

Если готового плагина нет, остаются два пути. Первый - делегирование проверки: имя _acme-challenge.example.com через CNAME уводят в отдельную проверочную зону, которую обслуживает маленький помощник вроде acme-dns:

_acme-challenge.example.com.  CNAME  _acme-challenge.auth.example.org.

У делегирования есть и практический бонус для компаний с несколькими доменами: проверочные имена всех зон собираются в одном месте, и токен к основному DNS не выдаётся вообще. Утечка секрета от проверочной зоны не даёт ничего, кроме права публиковать TXT записи в ней.

Второй путь - режим manual со скриптами. Certbot вызывает --manual-auth-hook перед проверкой и --manual-cleanup-hook после, а скрипты сами дергают API провайдера чем угодно, хоть curl-ом. Без скриптов режим manual означает вставку TXT записей руками, и автоматическое продление для него невозможно в принципе: таймер будет честно останавливаться в ожидании человека.

Частые ошибки DNS проверки и правила лимитов Let's Encrypt

Самая частая неприятность - время. Запись создана, а центр её не видит: зона обновилась не везде, а плагин ждать не стал. Провайдеры держат DNS на распределённой сети серверов, и предсказать момент, когда значение долетит до всех узлов, сложно; для этого и существует параметр ожидания. Лечение простое: поднять --propagation-seconds до 60-120 и посмотреть на запись своими глазами:

dig TXT _acme-challenge.example.com +short

Вторая по частоте - права токена. Если центр авторизации и API обмениваются отказами, у токена нет прав DNS Edit на нужную зону, значение скопировано с потерянным хвостом или флаг --credentials смотрит не в тот файл. Новый токен с единственной правкой на одну зону исключает путаницу.

Третья семья - записи CAA и делегирование. CAA перечисляет, каким центрам зона разрешает выпуск сертификатов; если центра Let's Encrypt в списке нет, выпуск отклоняется. Правильная запись выглядит так:

example.com.  CAA  0  issue "letsencrypt.org"

Кривой CNAME делегирования уводит проверку в никуда, поэтому после настройки делегирования делают контрольный сухой прогон. Отдельная ловушка - забытый корень: сертификат *.example.com на адресе example.com не действует, и если корневое имя не добавили в выпуск, сайт на корне останется с ошибкой соединения. Лечится перевыпуском с обоими именами.

Лимиты касаются и wildcard. На зарегистрированный домен выдаётся до 50 новых сертификатов за 7 дней, на точный набор имён - 5 за неделю, на учётную запись - 300 новых заказов за 3 часа. Продление через ARI лимитов не расходует; продление тем же набором имён без ARI освобождено от части лимитов, но не от лимита на набор. Для тренировок существует тестовый центр с мягкими правилами: сертификаты оттуда недоверенные, зато и волноваться не о чем.

Wildcard с DNS проверкой - история из разряда "настроил и забыл", но при двух условиях: токен с минимальными правами и контрольный прогон раз в квартал. Тогда звёздочный сертификат перестаёт быть событием: поддомены плодятся, мониторинг зелёный, а единственное напоминание о сертификатах - строчка в журнале продления.