MAATRIX / Блог / Как сертификат Let's Encrypt доказывает, что домен ваш

Как сертификат Let's Encrypt доказывает, что домен ваш

MAATRIX

Когда вы запускаете certbot --nginx -d example.com, где-то за кулисами происходит настоящая проверка: удалённый сервер должен убедиться, что домен example.com действительно под вашим контролем, а не принадлежит кому-то другому. Никто не звонит вам и не просит паспорт — всё решает автоматический протокол за несколько секунд. Разберём, как именно Let's Encrypt доказывает себе, что домен ваш, почему для этого существует два разных способа и почему сертификат всё равно живёт всего 90 дней.

ACME: протокол, который не верит на слово

Let's Encrypt — это удостоверяющий центр (CA), который выпускает сертификаты бесплатно и полностью автоматически. Раньше выпуск SSL-сертификата требовал ручной проверки: вы отправляли запрос, кто-то в компании-CA проверял документы или письмо на admin@ваш-домен, и через день-два присылал файл сертификата. Let's Encrypt убрал человека из этого процесса целиком — и для этого понадобился протокол, который умеет доказывать владение доменом машинным способом. Этот протокол называется ACME (Automatic Certificate Management Environment), и сегодня это открытый стандарт (RFC 8555), которым пользуется не только Let's Encrypt, но и другие центры сертификации.

Идея ACME простая и честная: раз вы утверждаете, что владеете доменом, докажите это, разместив что-то, что мог разместить только владелец. «Что-то» — это уникальный токен, который CA сам генерирует для конкретного запроса и который клиент (certbot, acme.sh и подобные) должен опубликовать в одном из двух мест, подконтрольных только владельцу домена: либо на веб-сервере по определённому пути, либо в DNS-записях домена. CA затем сам идёт и проверяет, там ли токен. Если токен на месте — домен ваш, сертификат выпускается. Этот шаг называется challenge (проверочное задание), и именно от того, какой тип challenge вы выбрали, зависит, что конкретно нужно разместить и где.

Важный нюанс: ACME проверяет не «вы ли владелец организации example.com» (это делают дорогие OV/EV-сертификаты через ручную верификацию юрлица), а куда более узкую вещь — «контролируете ли вы прямо сейчас инфраструктуру, которая отвечает за этот домен»: веб-сервер за IP, на который смотрит DNS-запись, или саму DNS-зону. Это доменная валидация (DV), и её достаточно для шифрования трафика браузер-сервер — того, ради чего 99% сайтов вообще ставят HTTPS.

HTTP-01: файл-токен по известному пути

Самый распространённый способ — HTTP-01. Логика: если вы можете положить файл на веб-сервер, отвечающий за домен по HTTP, значит, вы контролируете этот сервер, а домен на него указывает через DNS — значит, домен ваш.

Механика по шагам:

  1. Клиент (например, certbot) сообщает CA: «хочу сертификат для example.com».
  2. CA генерирует случайный токен и присылает его клиенту в ответ.
  3. Клиент кладёт файл с этим токеном по строго определённому пути на веб-сервере:
http://example.com/.well-known/acme-challenge/<token>

Содержимое файла — тот же токен плюс отпечаток ключа аккаунта клиента (key authorization), так что подделать ответ, не зная приватный ключ аккаунта, нельзя.

  1. CA обращается по этому URL с обычных серверов Let's Encrypt (обычно с нескольких точек одновременно, чтобы отсечь атаки через подмену маршрута для конкретного наблюдателя) и сверяет, что там лежит.
  2. Если содержимое совпало — challenge пройден, домен подтверждён, CA подписывает сертификат.

Certbot делает эти шаги прозрачно: с флагом --nginx или --apache он на время проверки сам создаёт нужный файл через временный конфиг веб-сервера (или через встроенный мини-сервер, если веб-сервер ещё не настроен), а после проверки убирает временные файлы. Вручную это выглядело бы так:

mkdir -p /var/www/html/.well-known/acme-challenge/
echo "токен.отпечаток-ключа" > /var/www/html/.well-known/acme-challenge/токен

Ограничение HTTP-01 очевидно из его же механики: он подтверждает только конкретный домен или поддомен, к которому смог достучаться по HTTP на порту 80. Для example.com и www.example.com нужно два отдельных challenge (certbot делает это одним запуском, но под капотом — раздельные проверки). А для wildcard-сертификата *.example.com, который должен покрывать любой возможный поддомен, HTTP-01 в принципе не подходит — нельзя разместить файл на сервере поддомена, который ещё не существует.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

DNS-01: TXT-запись и единственный путь к wildcard

Второй способ — DNS-01. Идея та же: разместить токен там, куда доступ есть только у владельца домена, но вместо веб-сервера используется сама DNS-зона.

Механика:

  1. CA присылает клиенту токен (как и в HTTP-01, но конкретное значение отличается — это хеш от key authorization).
  2. Клиент должен создать в DNS-зоне домена TXT-запись строго с именем _acme-challenge.example.com и значением этого токена:
_acme-challenge.example.com.  300  IN  TXT  "значение-токена"
  1. CA запрашивает эту TXT-запись через обычный DNS-резолвинг и сверяет значение.
  2. Если совпало — challenge пройден.

Ключевое преимущество DNS-01 — он подтверждает контроль над всей зоной домена целиком, а не над конкретным поддоменом. Именно поэтому это единственный способ выпустить wildcard-сертификат *.example.com: CA не может обойти все возможные поддомены по HTTP, но одна TXT-запись в зоне доказывает, что вы управляете DNS для всего домена, а значит, вправе получить сертификат сразу на любые его поддомены.

DNS-01 также выручает там, где HTTP-01 бессилен в принципе: сервер вообще не выставлен наружу по 80/443 (внутренний сервис за VPN, приложение только с TLS-терминацией на другом узле), или домен временно недоступен по HTTP, но DNS-зоной вы управляете. Плата за гибкость — DNS-01 нельзя настроить одной командой без API: клиенту нужен доступ к вашему DNS-провайдеру (через API-ключ), чтобы создавать TXT-записи автоматически при каждом продлении. Именно здесь acme.sh обычно удобнее Certbot — он поддерживает большое число DNS-провайдеров «из коробки» через плагины, тогда как Certbot полагается на отдельные dns-плагины, которых меньше и которые не всегда актуальны для конкретного регистратора. Подробное сравнение того, что выбрать под свой сценарий, — в статье Certbot или acme.sh: что выбрать для сервера.

Почему сертификат живёт всего 90 дней

Коммерческие CA годами продавали сертификаты на 1-2 года. Let's Encrypt сознательно выбрал короткий срок — 90 дней — и это не техническое ограничение протокола ACME, а осознанное архитектурное решение, у которого две причины.

Первая — принуждение к автоматизации. Если сертификат живёт два года, обновление становится редким ручным ритуалом: администратор один раз настраивает HTTPS, забывает про него, а через два года сайт падает с ошибкой просроченного сертификата, потому что никто не вспомнил вовремя. Короткий срок в 90 дней делает ручное продление невыносимым для сколько-нибудь заметного числа доменов — и вынуждает с самого начала настраивать автоматическое продление через cron или systemd-таймер. В результате «сертификат истёк» становится редкой ошибкой конфигурации автопродления, а не забывчивости человека.

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

Certbot и acme.sh по умолчанию пытаются продлить сертификат не в последний день, а заранее — обычно в последнюю треть срока действия, то есть примерно за 30 дней до истечения. Это оставляет запас: если при очередной попытке продления что-то пошло не так (упал DNS-провайдер, сменился IP, закрылся порт), у вас есть недели, а не часы, чтобы это заметить и починить, прежде чем сертификат реально станет недействительным. Если продление всё же не сработало и сертификат просрочился, разбор конкретных причин и починка — в статье SSL-сертификат не обновился.

Certbot и acme.sh: как автопродление работает на практике

И Certbot, и acme.sh при установке сами регистрируют задачу автопродления — вам не нужно писать cron-задание вручную.

Certbot ставит systemd-таймер (или cron-задачу на старых системах), который дважды в сутки запускает certbot renew. Эта команда сама проверяет срок действия каждого установленного сертификата и обновляет только те, что приближаются к истечению — остальные она просто пропускает, так что частый запуск не создаёт лишней нагрузки на CA. Проверить, что таймер стоит:

systemctl list-timers | grep certbot

Протестировать продление без реального изменения сертификата (dry-run):

certbot renew --dry-run

acme.sh устанавливает свою cron-задачу при первой установке (обычно в crontab -l появляется вызов acme.sh --cron), которая тоже сама решает, какие сертификаты пора обновлять, исходя из их фактического срока действия. Если у вас DNS-01 через API провайдера, acme.sh при продлении сам заново создаёт нужную TXT-запись, дожидается её появления и убирает после проверки — это стоит проверить один раз вручную после установки, прежде чем полагаться на автоматику:

acme.sh --renew -d example.com --dns dns_cf --force

Оба клиента после выпуска или продления сертификата умеют выполнять хук — команду для перезапуска или мягкой перезагрузки веб-сервера, чтобы тот подхватил новый сертификат (сам файл сертификата обновляется на диске, но nginx или apache держат старый в памяти, пока их не перезагрузить). У Certbot это делается флагом --deploy-hook, у acme.sh — параметром --reloadcmd:

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

Если вы ещё не устанавливали Let's Encrypt на сервере и решаете, с чего начать, пошаговая установка с Certbot и Nginx — в статье Как установить и настроить Let's Encrypt SSL на VPS.

Типичные ошибки challenge и как их избежать

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

Для HTTP-01— файрвол или прокси блокирует порт 80. Если ufw, iptables или облачный файрвол провайдера закрывают входящий 80/tcp (например, вы намеренно разрешили только 443, полагая, что HTTP не нужен), запрос CA на /.well-known/acme-challenge/ не проходит, и challenge падает с ошибкой вроде Connection refused или таймаутом. Порт 80 должен быть открыт снаружи именно на момент проверки (для HTTP-01 это требование протокола: он всегда идёт по 80, редирект на HTTPS не мешает, а вот полностью закрытый порт — мешает). Проверить снаружи:

curl -I http://ваш-домен/.well-known/acme-challenge/test

Если ответ не приходит вообще (не 404, а именно обрыв соединения) — дело в файрволе, а не в веб-сервере. Похожая ловушка — Cloudflare в режиме проксирования («оранжевое облако»): запрос CA может уходить не на ваш сервер напрямую, а через инфраструктуру Cloudflare, и если та не настроена пропускать /.well-known/acme-challenge/ к origin-серверу прозрачно, проверка бьётся. Решение — на время выпуска переключить домен в DNS-only («серое облако») либо сразу использовать DNS-01, который прокси не касается вообще.

Для DNS-01 — DNS ещё не распространился. TXT-запись _acme-challenge создана, но CA запрашивает её раньше, чем изменение дошло до резолверов, которые он использует, — из-за TTL записи, кеширования на промежуточных серверах или задержки самого DNS-провайдера в применении изменений через API. Клиент (certbot с DNS-плагином, acme.sh) обычно ждёт какое-то время после создания записи перед тем, как сообщить CA «готово», но если провайдер медленный, этой паузы может не хватить. Проверить, видна ли запись публично, ещё до вызова клиента:

dig +short TXT _acme-challenge.ваш-домен @8.8.8.8

Если запись не видна через публичный резолвер вроде 8.8.8.8, значит, она либо ещё не распространилась, либо создана не в той зоне (частая ошибка — TXT-запись ушла не в основную зону домена, а в зону, которую всё ещё обслуживает старый регистратор, если DNS недавно переносили). Для низкого TTL на самой записи _acme-challenge (60-300 секунд) распространение обычно укладывается в разумное время, но конкретная скорость зависит от вашего DNS-провайдера, и универсального числа секунд, которое подойдёт всем, здесь нет — при нестабильном автопродлении по DNS-01 разумно один раз вручную замерить, сколько у вас уходит времени между записью через API и видимостью через публичный резолвер, и заложить такой запас в хук клиента.

Более широкий разбор ошибок Let's Encrypt на практике — не только challenge, но и rate limit, mixed content после выпуска и другие частые ситуации — в статье Let's Encrypt SSL на сервере: частые ошибки и решения.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать VPS

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Можно ли пройти и HTTP-01, и DNS-01 для одного домена?

Да, это не взаимоисключающие способы, а варианты challenge для конкретного запроса. Для одного и того же домена в разное время можно использовать разный способ — например, обычный сертификат через HTTP-01, а wildcard для него же — через DNS-01.

Нужно ли держать порт 80 открытым постоянно ради HTTP-01?

Да, если вы продолжаете использовать HTTP-01 для автопродления — CA будет обращаться на 80-й порт при каждой попытке продления, а не только при первом выпуске. Если держать 80 закрытым принципиально, переходите на DNS-01.

Что будет, если сертификат всё же истечёт?

Браузер покажет предупреждение о недоверенном соединении, а не молча пропустит трафик — сайт для посетителей станет недоступен через HTTPS. Это ещё один аргумент за настроенное и проверенное автопродление, а не разовый выпуск сертификата.

Отличается ли проверка домена для wildcard-сертификата на несколько уровней поддоменов, например *.sub.example.com?

Механика та же — TXT-запись в _acme-challenge соответствующей зоны, но wildcard покрывает только один уровень поддоменов от точки, на которой он выпущен. Для *.sub.example.com и отдельно для sub.example.com может понадобиться либо отдельный SAN в сертификате, либо отдельный wildcard.

Могу ли я использовать один и тот же ACME-аккаунт для нескольких доменов и серверов?

Да, аккаунт в Let's Encrypt не привязан к одному домену — это просто пара ключей, которой подписываются запросы. Certbot и acme.sh создают аккаунт один раз на клиенте и переиспользуют его для всех доменов, которые вы выпускаете с этого сервера.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →