Let's Encrypt и certbot: автообновление сертификатов на nginx
Сертификат Let's Encrypt живёт 90 дней. Если ты один раз настроил его руками и забыл — через три месяца сайт встретит пользователей предупреждением о просроченном сертификате. Поэтому автообновление я делаю сразу, в тот же день, когда ставлю TLS.
Расскажу, как у меня это устроено на типичном Ubuntu-сервере с nginx. Конфигурация устойчивая: пережила два миграта, обновление nginx и переезд между хостерами.
Установка certbot
В свежих Ubuntu я ставлю certbot из snap, потому что в обычных репозиториях он отстаёт от апстрима. Но в большинстве рабочих кейсов и apt-овой версии хватает. Покажу обе.
sudo apt update
sudo apt install certbot python3-certbot-nginxЧерез snap:
sudo snap install core
sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbotПолучение сертификата
Если nginx уже отвечает на 80 порту по нужному домену, проще всего использовать webroot или плагин nginx. Я предпочитаю --webroot, потому что он не лезет в конфиг nginx и не перезагружает его в самые неудобные моменты.
Готовим директорию для ACME-челленджа:
sudo mkdir -p /var/www/certbotВ nginx-конфиге обязательно должен быть блок:
server {
listen 80;
server_name example.com www.example.com;
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://example.com$request_uri;
}
}Дальше выпускаем сертификат:
sudo certbot certonly \
--webroot -w /var/www/certbot \
-d example.com -d www.example.com \
--email admin@example.com \
--agree-tos --no-eff-emailВ случае успеха ключ и цепочка лежат в /etc/letsencrypt/live/example.com/. Подключаешь их в nginx и делаешь sudo systemctl reload nginx.
Автообновление: systemd-таймер
Современный certbot из коробки ставит systemd-таймер certbot.timer. Проверить можно так:
systemctl list-timers | grep certbotВывод — что-то вроде certbot.timer с расписанием два раза в день. Это нормально: certbot реально продлевает сертификат не каждый запуск, а только когда до окончания меньше 30 дней. Просто чаще проверяет.
Если таймера нет (например, ты ставил из снэпа очень старой версии или из самописных deb), легко поднять руками.
# /etc/systemd/system/certbot-renew.service
[Unit]
Description=Certbot renew
[Service]
Type=oneshot
ExecStart=/usr/bin/certbot renew --quiet --deploy-hook "systemctl reload nginx"# /etc/systemd/system/certbot-renew.timer
[Unit]
Description=Run certbot renew twice a day
[Timer]
OnCalendar=*-*-* 03,15:14:00
RandomizedDelaySec=3600
Persistent=true
[Install]
WantedBy=timers.targetsudo systemctl daemon-reload
sudo systemctl enable --now certbot-renew.timerСамое важное: deploy-hook
Сертификат сам по себе обновляется без проблем. Но nginx продолжит держать в памяти старый файл, пока его не попросишь перечитать. Поэтому хук --deploy-hook "systemctl reload nginx" — это не косметика, а обязательная часть. Без него сертификат на диске свежий, а nginx по-прежнему отдаёт старый, и через 89 дней сайт ляжет с warning'ом.
Можно прописать хук глобально в файле /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh:
#!/bin/sh
systemctl reload nginxsudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.shТогда любой certbot renew после удачного апдейта будет вызывать reload автоматически.
Проверка, что всё реально работает
Главный тест — --dry-run:
sudo certbot renew --dry-runЭта команда повторяет всю процедуру обновления, но запрашивает у Let's Encrypt тестовый сертификат и не пишет его на диск. Если она прошла без ошибок — настоящий рестарт через 60 дней тоже пройдёт.
Проверка дат сертификатов на сервере:
sudo certbot certificatesА ещё я люблю смотреть из-вне:
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -datesЧто чаще всего ломается
80 порт закрыт фаерволом или отдан другому сервису
ACME-челлендж ходит ровно туда: http://example.com/.well-known/acme-challenge/.... Если 80 закрыт фаерволом или его слушает не nginx, обновление упадёт. Лечится либо открытием порта, либо переходом на --dns-плагин.
nginx не reload-ится после продления
Самая частая причина — забытый deploy-hook. Симптомы простые: сертификат на диске обновился (видно по certbot certificates), а в браузере всё ещё старый. После добавления хука всё лечится.
Просрочили — что делать
Если сертификат уже просрочен, обычный certbot renew может не сработать в зависимости от срока. В таком случае запускаю выпуск заново certbot certonly --webroot ... с теми же доменами и форсю перевыпуск:
sudo certbot certonly --webroot -w /var/www/certbot \
-d example.com -d www.example.com \
--force-renewalЧто я делаю на каждом новом сервере
Чтобы потом не возвращаться:
- ставлю certbot;
- выпускаю сертификат через webroot;
- добавляю файл в
renewal-hooks/deploy/с reload nginx; - гоняю
certbot renew --dry-runи смотрю, что прошло; - проверяю, что таймер активен.
Потом про сертификат можно забыть на годы. Возвращаешься к нему только когда меняешь домены или переезжаешь на другой сервер.