Сертификаты центра Let's Encrypt действуют девяносто дней, и ручное продление раз в квартал заканчивается просрочкой в неподходящий момент. Процесс автоматизируется полностью: утилита получает сертификат, подтверждает владение доменом, заменяет файлы и подаёт серверу сигнал перечитать конфигурацию. Разбираем три способа подтверждения, настройку продления и сценарий применения сертификата в Angie или Nginx.
Как устроен выпуск сертификата и какие бывают проверки
Центр выпускает сертификат после доказательства управления доменом, и способ доказательства определяет всю дальнейшую настройку.
Первый способ, проверка по HTTP: центр запрашивает файл по известному пути на порту 80. Способ прост, но требует открытого порта и работает только для конкретных имён, без шаблонов.
Второй способ, проверка по TLS на порту 443: сервер отвечает на особое согласование, подтверждающее владение именем. Способ подходит при закрытом порту 80, но требует монопольного доступа к порту 443.
Третий способ, проверка по DNS: заявитель добавляет текстовую запись с выданным значением. Так выпускают сертификат на шаблонное имя, и способ работает при закрытых портах, но требует доступа к управлению зоной.
sudo apt install certbot python3-certbot-nginx
certbot --version
Перед первым выпуском проверяют, что имя указывает на нужную машину и порт доступен снаружи: половина неудач объясняется нераспространённой записью либо закрытым портом.
dig +short app.example.ru
sudo ss -tlnp | grep -E ':80|:443'
Выпуск сертификата через веб-каталог и через модуль сервера
Способ с веб-каталогом не вмешивается в конфигурацию сервера и потому предсказуем: утилита кладёт файл в указанный каталог, а сервер отдаёт его по известному пути.
sudo mkdir -p /var/www/letsencrypt
certbot certonly --webroot -w /var/www/letsencrypt \
-d app.example.ru -d www.app.example.ru \
--email Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. --agree-tos --no-eff-email
Соответствующее место в конфигурации сервера описывают заранее.
server {
listen 80;
server_name app.example.ru www.app.example.ru;
location /.well-known/acme-challenge/ {
root /var/www/letsencrypt;
default_type "text/plain";
}
location / {
return 301 https://$host$request_uri;
}
}
Путь проверки начинается с точки, и некоторые конфигурации отклоняют запросы к скрытым каталогам, поэтому при корректных правах причину ищут в общих правилах.
Способ с модулем сервера поручает утилите правку конфигурации: она добавляет слушатель на порт 443 и пути к файлам.
sudo certbot --nginx -d app.example.ru --redirect
Удобство сочетается с риском: при неудачной автоматической правке восстановление требует резервной копии, поэтому для рабочей среды чаще выбирают способ с веб-каталогом.
Файлы утилита раскладывает в каталоге с именем домена и обновляет на месте, поэтому пути не меняются от продления к продлению.
sudo ls -l /etc/letsencrypt/live/app.example.ru/
sudo openssl x509 -in /etc/letsencrypt/live/app.example.ru/fullchain.pem -noout -dates
Подключение выполняют двумя файлами: цепочкой с сертификатом сайта и промежуточными и закрытым ключом. Без цепочки часть клиентов не построит доверие и покажет предупреждение.
server {
listen 443 ssl;
http2 on;
server_name app.example.ru;
ssl_certificate /etc/letsencrypt/live/app.example.ru/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.ru/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
}
Подтверждение по записи DNS и сертификат на шаблонное имя
Выпуск для шаблонного имени возможен только через DNS. Если зона у провайдера с программным интерфейсом, устанавливают соответствующий модуль, и запись добавляется автоматически.
sudo apt install python3-certbot-dns-cloudflare
sudo certbot certonly --dns-cloudflare \
--dns-cloudflare-credentials /etc/letsencrypt/cf.ini \
-d example.ru -d '*.example.ru' \
--email Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript. --agree-tos
Файл с учётными данными хранят вне каталога сертификатов и закрывают правами только для владельца.
# /etc/letsencrypt/cf.ini
dns_cloudflare_api_token = значение
sudo chmod 600 /etc/letsencrypt/cf.ini
Без программного интерфейса у держателя зоны применяют ручной режим: он пригоден для первого выпуска, но не для автоматического продления.
sudo certbot certonly --manual --preferred-challenges dns \
-d example.ru -d '*.example.ru'
Утилита выводит значение записи и имя, которое нужно создать, и ждёт подтверждения. Распространение записи проверяют запросом к публичному серверу имён.
dig +short TXT _acme-challenge.example.ru @1.1.1.1
Постоянная запись с фиксированным значением не подходит, поскольку значение меняется при каждом выпуске, поэтому для продления без участия человека нужен программный доступ к зоне.
Когда зона управляется внутри компании, для проверки создают отдельное делегирование имени acme на собственный сервер, и выпуск работает без доступа к основной зоне.
Автоматическое продление и проверка механизма
Утилита устанавливает задачу по расписанию, которая выполняется дважды в сутки и продлевает сертификаты с остатком срока меньше тридцати дней.
systemctl list-timers | grep certbot
sudo certbot renew --dry-run
Команда с ключом dry-run имитирует продление на тестовом окружении центра и не меняет действующие файлы. Ею и проверяют схему: прошла имитация пройдёт и настоящее продление.
При отсутствии системного планировщика задачу добавляют вручную.
echo '17 3,15 * * * root certbot renew --quiet --deploy-hook "systemctl reload angie"' \
| sudo tee /etc/cron.d/certbot-renew
Параметр renew не трогает сертификаты с большим остатком срока, поэтому частый запуск безопасен и не исчерпывает лимиты центра.
Файл с параметрами продления хранит способ проверки, список имён и сценарии. Править его стоит осознанно: без правки файла задача попытается повторить прежний способ.
sudo cat /etc/letsencrypt/renewal/app.example.ru.conf
Журнал выполнения задачи просматривают после каждого изменения конфигурации сервера: правка мест расположения способна сломать путь к каталогу проверки.
Срок действия и остаток проверяют одной командой по каждому выпущенному сертификату.
sudo certbot certificates
Применение нового сертификата в Angie и Nginx без простоя
Продление заменяет содержимое файлов, но сервер держит старый сертификат в памяти до перечитывания конфигурации, поэтому после выпуска подают сигнал автоматически запускаемым сценарием.
#!/bin/sh
# /etc/letsencrypt/renewal-hooks/deploy/reload-web.sh
set -e
if systemctl is-active --quiet angie; then
systemctl reload angie
elif systemctl is-active --quiet nginx; then
systemctl reload nginx
fi
exit 0
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-web.sh
sudo certbot renew --dry-run
Сценарии в каталоге renewal-hooks выполняются в трёх точках: до начала продления, после успешного выпуска и при ошибке. Для перезагрузки подходит каталог deploy, он вызывается только при фактическом обновлении.
Сигнал перечитывания не обрывает существующие соединения: прежние процессы завершают текущие запросы, а новые работают с обновлёнными файлами, поэтому перезагрузка безопасна в любое время.
sudo angie -t && sudo systemctl reload angie
sudo systemctl status angie --no-pager | head -n 5
Проверка конфигурации перед перечитыванием обязательна: если файлы недоступны, новый рабочий процесс не запустится и сервер продолжит работу со старым сертификатом, что выглядит как успешное продление без применения.
При нескольких экземплярах сервера за одним именем перезагружают каждый, иначе часть процессов продолжит отдавать прежний сертификат. Сценарий перезагрузки расширяют перечислением служб.
Права на закрытый ключ расширять не нужно, и давать рабочему пользователю сервера доступ к нему нельзя. Главный процесс веб-сервера запускается от имени администратора и читает закрытый ключ на старте, а рабочие процессы порождаются уже от непривилегированного пользователя. Ключ читается один раз и именно главным процессом, поэтому штатных прав достаточно.
Каталоги live и archive утилита создаёт закрытыми для всех, кроме администратора, и таким набор прав должен оставаться. Если открыть ключ рабочему пользователю, уязвимость веб-приложения позволит нападающему забрать ключ сайта и подменять соединение для посетителей.
sudo ls -ld /etc/letsencrypt/live /etc/letsencrypt/archive
sudo ls -l /etc/letsencrypt/archive/app.example.ru/
sudo openssl pkey -in /etc/letsencrypt/live/app.example.ru/privkey.pem -noout -check
Первые две команды показывают ограничение доступа, третья проверяет целостность ключа без его вывода. Отдельно убеждаются, что непривилегированному пользователю ключ не читается: ожидаемый ответ здесь отказ в доступе.
Права теряются при переносе сертификатов между машинами, и это частая причина отказа после миграции: файл копируют с другим владельцем либо в открытый каталог, после чего сервер не может его прочитать.
Если отдельная программа должна раскладывать сертификат по другим службам, доступ ей дают точечно списком доступа, а не расширением прав на весь каталог.
sudo setfacl -m u:deployer:r /etc/letsencrypt/archive/app.example.ru/privkey1.pem
sudo setfacl -m x /etc/letsencrypt/archive /etc/letsencrypt/archive/app.example.ru
getfacl -p /etc/letsencrypt/archive/app.example.ru/privkey1.pem
Вторая строка обязательна: без права входа в каталог чтение файла невозможно, даже если доступ к самому файлу выдан. Третья команда показывает итоговый набор прав, и по нему видно, что рабочий пользователь сервера в списке не появился.
Такой подход сохраняет главное свойство схемы: компрометация веб-приложения, работающего от непривилегированного пользователя, не даёт доступа к закрытому ключу, а значит не позволяет подменить соединение для посетителей сайта.
Лимиты центра, тестовое окружение и перенос сертификатов
Центр ограничивает число выпусков. Лимиты считаются по доменам второго уровня и по точным именам, и неудачные попытки быстро их исчерпывают, поэтому отладку ведут на тестовом окружении.
sudo certbot certonly --webroot -w /var/www/letsencrypt \
-d test.example.ru --test-cert --agree-tos -m Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в браузере должен быть включен Javascript.
Тестовые сертификаты не принимаются браузерами, но позволяют проверить всю цепочку: доступность каталога, права, работу сценария перезагрузки и расписание. Лимиты тестового окружения отделены от основных.
Перенос на другую машину выполняют копированием каталога с действующими файлами и каталога с параметрами продления, после чего проверяют права и запускают имитацию. Для одного имени часто проще выпустить сертификат заново.
На старой машине задачу продления отключают: две машины, продлевающие одно имя, создают лишние выпуски и приближают исчерпание лимита.
sudo systemctl disable --now certbot.timer
Контроль сроков и реакция на сбой
Автоматика снижает риск, но не отменяет контроля: проверка срока выполняется по файлу сертификата.
#!/bin/sh
# проверка, что до окончания осталось больше 14 дней
DAYS=14
for f in /etc/letsencrypt/live/*/fullchain.pem; do
end=$(openssl x509 -in "$f" -noout -enddate | cut -d= -f2)
left=$(( ( $(date -d "$end" +%s) - $(date +%s) ) / 86400 ))
[ "$left" -lt "$DAYS" ] && echo "$f: осталось $left дн."
done
Запуск по расписанию с отправкой результата в систему оповещения закрывает вопрос: сообщение придёт задолго до истечения срока.
Помимо срока контролируют факт успешного завершения задачи: проверяют свежий журнал выполнения за сутки и отсутствие в нём отказов.
В журнале утилиты видны причины неудач: недоступность каталога проверки, неверный ответ сервера имён, отказ при проверке прав и исчерпание лимита.
sudo tail -n 50 /var/log/letsencrypt/letsencrypt.log
Полная картина состоит из трёх проверок: задача по расписанию запускается, имитация продления проходит без ошибок, срок действующего сертификата больше порога. Если все три условия выполняются, о продлении можно забыть.