Daily MaverickJoburg crisis cannot be solved without competent City leadershipESPNPremier League depth charts for most popular teams: Who is key?The Jerusalem PostWho will teach the next generation?PunchNigeria@66: 2027 election will test our democratic maturity – JonathanBollywood HungamaDisha Patani completes a decade in Bollywood as MS Dhoni: The Untold Story turns 10: “Beyond grateful ”InquirerBukidnon’s Quezon town marks 10th Senior Citizens’ DayGhaflaImages reveal extent of Wicknell Chivayo helicopter crash wreckageIl Fatto Quotidiano“Il lusso? Se c’è non me lo nego ma non lo cerco. Non ho lo yatch di 40 metri o la Lamborghini. Il mio aspetto? C’è il fascino sottile della decomposizione delle carni”: Paolo Bonolis si racconta3DNewsЯпонский Google изобрёл вертикальную клавиатуру-конвейер Gboard Kuru-Kuru — построить её может любой желающийسكاي نيوز عربيةاستطلاع يفجر مفاجأة بشأن رونالدو.. ماذا يريد مشجعو البرتغال؟RTBF InfoDes feux et dégradations lors d'un mouvement spontané d'élèves devant plusieurs écoles de LiègeBBC News BrasilGoogle remove mais de 40 canais com falsos médicos de IA após reportagens da BBC News Brasil
The Daily Newsstand · Free, Always
Thursday, October 1, 2026

certbot говорил VALID, браузер говорил «небезопасно». Правы были оба

Translate

12 сентября я зашёл на собственный сайт с телефона и получил полноэкранное предупреждение о небезопасном соединении.

Сертификат parkout.ru истёк 22 августа. То есть 21 день каждый посетитель видел эту страницу вместо карты.

Первая мысль была про certbot: не продлил, кончилось место, сломался таймер. Полез проверять.

Certificate Name: parkout.ru
  Expiry Date: 2026-10-21 (VALID: 39 days)

Certbot был в полном порядке. Новый сертификат он выпустил ещё 23 июля, лежит на диске, действует до 21 октября.

Браузер при этом показывал истёкший.

Где расходятся две правды

Обе стороны говорили правду, просто о разных вещах.

Certbot знает про файл на диске. Он его выпустил, положил в /etc/letsencrypt/live/ и честно отчитался.

Браузер получает то, что держит в памяти процесс nginx. А nginx читает сертификат один раз, при старте, и дальше работает с тем, что загрузил. Обновление файла для него не событие.

Контейнер nginx-lb не перезапускался с 21 июля. Сертификат он загрузил тогда же, старый, с датой истечения 22 августа. Дальше certbot мог обновлять файл сколько угодно: процесс продолжал отдавать то, что прочитал в июле.

Связать одно с другим должен был deploy-hook — скрипт, который certbot запускает после успешного обновления. Заглянул туда:

$ ls /etc/letsencrypt/renewal-hooks/deploy/
$

Пусто. Хуков не было вообще, ни одного, с самого начала.

Схема «certbot на хосте, nginx в контейнере» разваливается ровно здесь. Если certbot стоит с плагином --nginx, он перезагружает nginx сам, и про хуки можно не знать годами. Как только nginx уезжает в docker, этот плагин применять не к чему: снаружи контейнера ему нечего перезагружать, и связь рвётся молча.

Починка на минуту:

docker exec nginx-lb nginx -s reload

И скрипт, чтобы это происходило само:

#!/bin/bash
# /etc/letsencrypt/renewal-hooks/deploy/reload-nginx-lb.sh
set -e
LOG=/var/log/parkout-certbot-hook.log

docker inspect -f '{{.State.Running}}' nginx-lb 2>/dev/null | grep -q true || {
    echo "$(date -Is) deploy-hook: контейнер nginx-lb не запущен" >> "$LOG"
    exit 0
}
docker exec nginx-lb nginx -t 2>>"$LOG" || {
    echo "$(date -Is) deploy-hook: конфиг невалиден, reload отменён" >> "$LOG"
    exit 1
}
docker exec nginx-lb nginx -s reload
echo "$(date -Is) deploy-hook: nginx-lb reloaded" >> "$LOG"

Проверка nginx -t перед reload тут не формальность. Если конфиг успел сломаться между запусками, reload на битом конфиге уронит живой nginx, и вместо истёкшего сертификата получится отсутствующий сайт.

Мониторинг всё видел

Дальше начинается вторая часть, которая мне нравится меньше.

У нас стоит Uptime Kuma. Она эту аварию зафиксировала. Down Count 2319, повторные уведомления раз в час, три недели подряд.

Я не увидел ни одного письма.

Причина оказалась не в SMTP и не в Kuma. Алерты уходили на dymatrosov@parkout.ru, а этот ящик никто не открывает. Еженедельные письма про бэкапы приходили на личный gmail и прекрасно читались — поэтому ощущение «мониторинг работает» было, а мониторинга по факту не было.

Дальше я потратил час на неверную гипотезу, и она поучительная.

В настройках Kuma поле smtpFrom было заполнено как "Uptime Kuma ParkOut", без адреса в угловых скобках. Невалидный From. Я решил, что почтовый сервер такие письма отбрасывает, и пошёл чинить.

Проверил живой отправкой, прежде чем править. Gmail такое письмо принимает без единой жалобы. Гипотеза оказалась неверной, и хорошо, что я её проверил, а не «исправил».

Заодно выяснилось, почему мои первые попытки проверить SMTP давали 535 BadCredentials. Пароль в .env лежит в кавычках:

EMAIL_APP_PASSWORD="xxxx xxxx xxxx xxxx"

Bash при source кавычки снимает, и приложение получает правильные 19 символов. А мой скрипт вытаскивал значение через grep -oP и захватывал кавычки вместе с паролем: 21 символ вместо 19. Пароль был валиден всё это время, врал мой способ его прочитать.

Исправление в итоге свелось к одной строке в базе Kuma: получатель на читаемый адрес, нечитаемый в копию.

Чем это закончилось

Обе правки я сделал 12 сентября. И вот здесь важный момент, из-за которого я вообще решил это описать.

Починка, которую не проверили в бою, — это гипотеза. Хук выглядел правильным, лог писался, тестовый запуск проходил. Но настоящая проверка была одна: дождаться реального обновления сертификата и посмотреть, что произойдёт само.

Обновление пришло 22 сентября. В логе:

2026-09-22T06:12:25+03:00 deploy-hook: nginx-lb reloaded

Сегодня, 30 сентября, сертификат в браузере:

notBefore=Sep 22 02:13:51 2026 GMT
notAfter=Dec 21 02:13:50 2026 GMT

Выпущен 22 сентября, тот самый новый. Контейнер при этом не перезапускался с 12 сентября — nginx перечитал сертификат по сигналу, не теряя соединений.

Отдельно стоит сказать, почему это вообще доказательство. Старый сертификат действовал до 21 октября. Если бы хук не сработал, сегодня сайт всё равно открывался бы нормально, просто со старым сертификатом, и поломка всплыла бы только через три недели. Различить два состояния можно единственным способом: посмотреть дату выпуска того сертификата, который реально отдаётся наружу.

Что забрать с собой

certbot certificates не говорит о том, что видит пользователь. Он рассказывает про файл. Проверять надо снаружи, тем же способом, каким сайт видит браузер:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -dates

Эти две команды могут расходиться неделями, и расхождение ничем себя не проявляет.

Переезд процесса в контейнер рвёт связи, о существовании которых вы не знали. Плагин --nginx годами перезагружал nginx сам, и про deploy-hooks можно было не подозревать. После переезда в docker перезагружать ему стало нечего. Ошибки нет, лога нет, просто одна из сторон больше не разговаривает с другой.

Мониторинг, который пишет в непрочитанный ящик, равен его отсутствию. Причём ощущается он лучше отсутствия, потому что даёт ложную уверенность. Проверять надо не «настроены ли уведомления», а «дошло ли последнее до человека».

Проверяйте гипотезу до того, как чинить по ней. Я был уверен, что невалидный smtpFrom блокирует доставку. Одно тестовое письмо сняло вопрос за минуту. Если бы я сразу «починил» From, я бы приписал себе исправление проблемы, которой не было, и настоящая причина осталась бы на месте.

Починка без проверки в бою — гипотеза. Между «написал хук» и «хук работает» прошло десять дней и одно настоящее обновление сертификата. До 22 сентября у меня был не исправленный баг, а правдоподобно выглядящий скрипт.

Это пятая статья по следам одного проекта — карты загруженности парковок parkout.ru. Предыдущие: про чёрную карту без единой ошибки в консоли, про −1000% занятости в собственном датасете, про сам датасет и про метрику, которая стала хуже и от этого правдивее.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.