Ubuntu 24.04: сертифікати оновлено, але Nginx все ще віддає старий — чому і як виправити

Було корисно?

Ви виконали certbot renew. Він повідомив «success». Моніторинг все ще кричить «сертифікат закінчується за 2 дні».
Ви перевіряєте файлову систему і новий сертифікат на місці. Та браузери й досі бачать старий — іноді навіть із тієї ж машини.

Це не містика. Це набір дуже передбачуваних режимів помилок: неправильний шлях, неправильний серверний блок, відсутність перезавантаження,
балансувальник навантаження, який виконує TLS перед вами, або застарілий процес, який ніколи не відкривав файли заново. На Ubuntu 24.04 налаштування за замовчуванням
розумні, але навколишня екосистема (Snap, systemd, кілька інстансів Nginx, контейнери) робить легкою помилку з упевненістю.

Що насправді відбувається, коли «оновлено» ≠ «віддається»

Працюють дві окремі системи:

  • Видача/оновлення сертифікатів: ACME-клієнт (зазвичай Certbot, іноді lego/acme.sh) записує нові файли на диск.
  • Обслуговування TLS: Nginx читає файли сертифікатів у пам’ять при завантаженні конфігурації (старт або reload). Він не «бачить» автоматично нові байти на диску.

Тому режим помилки простий: оновлення пройшло успішно, але стек обслуговування не перемкнувся.
Хитрість у тому, що «стек обслуговування» може бути не тим процесом, який ви собі уявляєте. Це може бути:
Nginx на хості, Nginx у контейнері, Nginx у chroot, другий інстанс Nginx, зворотній проксі зверху або хмарний балансувальник, що завершує TLS ще до того, як трафік потрапляє до Nginx.

Ще одна тонкість: іноді Nginx перезавантажується правильно, але все одно віддає «неправильний» сертифікат через вибір блоків за SNI.
Це поширено, якщо у вас є сервер за замовчуванням, wildcard-сертифікати або універсальний блок, залишений автоматизацією.

Ваша мета — не «оновити сертифікат». Це легка частина. Ваша мета: довести, який процес відповідає за TLS для конкретного імені хоста, і довести, які файли він завантажив.

Жарт №1: Сертифікати як молоко — свіжість важлива, але ніхто не хоче бути тим, хто нюхає їх в продакшні о 2:00.

Швидкий план діагностики (перший/другий/третій)

Перший: підтвердьте, що бачать клієнти

  1. З машини поза вашою мережею (або використовуючи публічний зонд) перевірте представлені сертифікати для конкретного імені хоста.
  2. Запишіть видавця, серійний номер та дати notBefore/notAfter.
  3. Якщо сертифікат старий, не чіпайте Certbot одразу. Сервер віддає старі байти; оновлення може вже бути в порядку.

Другий: встановіть, хто завершує TLS

  1. Чи порт 443 на хості справді належить Nginx?
  2. Чи є балансувальник, CDN, WAF або контролер Ingress, що робить TLS перед Nginx?
  3. Чи є більше одного Nginx (хост + контейнер)?

Третій: зіставте конфіг Nginx → шлях до сертифікату → файл на диску → запущений процес

  1. Знайдіть server блок, що відповідає SNI-імені хоста.
  2. Перевірте шляхи в ssl_certificate і ssl_certificate_key.
  3. Перевірте, чи це посилання Let’s Encrypt live/ або «закріплено» на archive/.
  4. Перезавантажте Nginx і повторно перевірте зовні.
  5. Якщо reload не змінює подачу, ви перезавантажуєте неправильний Nginx або неправильну конфігурацію.

Це порядок пошуку вузького місця. Починайте з того, що бачать користувачі, потім рухайтесь всередину. Не починайте з перегляду логів Certbot.
Саме так годину доводять неправильну річ.

Цікавi факти та контекст (чому ця проблема повторюється)

  • Факт 1: Nginx не читає сертифікати автоматично заново з диска. Він завантажує їх при старті та при reload (грейсфул перезавантаження конфігурації).
  • Факт 2: Сертифікати Let’s Encrypt мають короткий термін життя (зазвичай 90 днів), це зроблено навмисно щоб зменшити ризик компрометації ключа.
  • Факт 3: Certbot підтримує дві директорії: /etc/letsencrypt/archive (версійовані файли) і /etc/letsencrypt/live (символьні посилання на «поточні» файли).
  • Факт 4: Досить поширений інцидент: «оновлення пройшло успішно, але reload зазнав невдачі». Лог оновлення каже OK; Nginx продовжує віддавати старий сертифікат днями.
  • Факт 5: На ранніх етапах TLS деякі сервери вимагали повних рестартів при зміні сертифікатів; грейсфул reload став практичною функцією надійності, а не лише зручністю.
  • Факт 6: SNI (Server Name Indication) дозволяє одному IP обслуговувати кілька сертифікатів. Якщо вибір SNI потрапляє в інший серверний блок, ви отримаєте неправильно сертифікат, навіть якщо правильний сертифікат існує.
  • Факт 7: Прийняття Snap-пакетів Certbot в Ubuntu змінило місця файлів для логів і іноді змінювало, як запускаються хук-скрипти при оновленні, особливо при змішуванні встановлень apt і snap.
  • Факт 8: OCSP stapling може ускладнити відладку: клієнти можуть кешувати або відображати статус stapling окремо від ланцюжка сертифікатів, тому «виглядає неправильно» не обов’язково про файл сертифікату.

Основні причини: звідки насправді береться старий сертифікат

1) Nginx ніколи не перезавантажувався (або reload зазнав невдачі)

Certbot може оновити без перезапуску нічого. Nginx продовжує працювати і віддавати те, що був завантажив минулого тижня.
Якщо у вас є deploy-hook, який має перезавантажувати Nginx, він може бути не налаштований, не запускатися або падати.
Ще гірше: nginx -s reload може вийти успішно, але ви перезавантажили інший бінар або інший інстанс, ніж той, що слухає 443.

2) Nginx вказує на неправильний шлях до файлу

Правильний шаблон — посилатися на /etc/letsencrypt/live/yourname/fullchain.pem і privkey.pem.
Якщо хтось захардкодив /etc/letsencrypt/archive/yourname/fullchain2.pem, він ніколи не оновиться автоматично.
Це працюватиме, доки не перестане. Це класичний випадок «працює до наступної ротації».

3) Обраний неправильний серверний блок (SNI mismatch)

Можливо, правильний сертифікат у одному серверному блоці, а дефолтний серверний блок містить застарілий сертифікат.
Клієнти, що заходять на example.com, отримують правильний сертифікат; клієнти на www.example.com або API-хост отримують дефолтний.
Або ваш перевірочник здоров’я використовує IP-адресу і не відправляє SNI, тому він завжди потрапляє в дефолт. Тоді ви «вирішуєте» не ту проблему.

4) TLS завершується десь іще

Якщо ви використовуєте хмарний балансувальник, CDN, зворотній проксі або Kubernetes ingress, Nginx може взагалі не обслуговувати TLS.
Оновлення на бекенді не має значення; фронт-двері все ще мають старий сертифікат.
Найдорожчі інциденти — ті, де всі дебажать неправильний шар з великим ентузіазмом.

5) Кілька інстансів Nginx, контейнери або різні порти

Ubuntu 24.04 без проблем може запускати:
Nginx на хості (systemd), Nginx у Docker (compose) і контролер ingress (Kubernetes).
Якщо два процеси слухають 443 на різних інтерфейсах (або один на хост-мережі, інший за NAT), ви можете цілий день «перезавантажувати» неправильний.

6) Проблеми з правами або форматом ключа

Certbot може оновити, але записати ключі з правами, які Nginx не може прочитати (менш поширено з типовими шляхами Let’s Encrypt, частіше з кастомними хуками або скопійованими файлами).
Або ви перейшли з RSA на ECDSA, змінили імена файлів і забули оновити конфіг Nginx. Тоді reload зазнає невдачі і Nginx продовжує тримати стару конфігурацію в пам’яті.

Практичні завдання: команди, виводи та рішення (12+)

Ось перевірки, які я справді запускаю, коли продакшн каже «старий сертифікат». Кожне завдання включає:
команду, що означає вивід, і рішення, яке ви приймаєте.

Завдання 1: Перевірте поданий сертифікат зовні (SNI увімкнено)

cr0x@server:~$ openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer -dates -serial
subject=CN = example.com
issuer=C = US, O = Let's Encrypt, CN = R11
notBefore=Dec  1 00:12:34 2025 GMT
notAfter=Feb 29 00:12:33 2026 GMT
serial=03A1B2C3D4E5F6

Значення: Це те, що бачать клієнти. Поле notAfter показує, чи воно старе.

Рішення: Якщо це ще старий термін, дійте далі. Якщо воно нове, ваш моніторинг може перевіряти інше ім’я хоста або кінцеву точку.

Завдання 2: Перевірка подачі сертифікату без SNI (поведінка сервера за замовчуванням)

cr0x@server:~$ openssl s_client -connect example.com:443 -showcerts </dev/null 2>/dev/null | openssl x509 -noout -subject -dates
subject=CN = default.invalid
notBefore=Sep  2 10:00:00 2025 GMT
notAfter=Dec  1 10:00:00 2025 GMT

Значення: Без SNI Nginx обирає сервер за замовчуванням для цього IP:port.

Рішення: Якщо сертифікат за замовчуванням застарів, виправте дефолтний серверний блок також або налаштуйте клієнтів/health checks щоб відправляли SNI.

Завдання 3: Визначте, який процес слухає 443

cr0x@server:~$ sudo ss -ltnp | grep ':443 '
LISTEN 0      511          0.0.0.0:443       0.0.0.0:*    users:(("nginx",pid=2147,fd=6),("nginx",pid=2146,fd=6))

Значення: Ви бачите, який бінар володіє сокетом.

Рішення: Якщо це не Nginx (або не той, який ви очікуєте), зупиніться і відслідкуйте цей процес. Не перезавантажуйте не той процес.

Завдання 4: Переконайтесь, що ви перезавантажуєте саме той Nginx, що працює

cr0x@server:~$ ps -fp 2147
UID          PID    PPID  C STIME TTY          TIME CMD
root        2147       1  0 Dec28 ?        00:00:02 nginx: master process /usr/sbin/nginx -g daemon on; master_process on;

Значення: Підтверджує шлях (/usr/sbin/nginx) і що systemd ймовірно ним керує.

Рішення: Використовуйте systemctl reload nginx для цього екземпляра; не запускайте інший бінар з образу контейнера.

Завдання 5: Перевірка конфігурації Nginx перед перезавантаженням

cr0x@server:~$ sudo nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Значення: Reload ймовірно пройде успішно. Якщо це падає, Nginx відмовиться завантажити нові шляхи/сертифікати.

Рішення: Спочатку виправте помилки конфігурації. Якщо nginx -t неуспішний, «reload» не оновить сертифікати.

Завдання 6: Перезавантажте Nginx та підтвердіть, що systemd прийняв

cr0x@server:~$ sudo systemctl reload nginx
cr0x@server:~$ systemctl status nginx --no-pager -l
● nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/usr/lib/systemd/system/nginx.service; enabled; preset: enabled)
     Active: active (running) since Sun 2025-12-28 09:10:12 UTC; 1 day 3h ago
       Docs: man:nginx(8)
    Process: 33910 ExecReload=/usr/sbin/nginx -g 'daemon on; master_process on;' -s reload (code=exited, status=0/SUCCESS)

Значення: Reload виконався і завершився успішно.

Рішення: Заново перевірте подачу сертифікату зовні. Якщо нічого не змінилось, ви дивитесь на неправильний TLS-термінатор або неправильний серверний блок.

Завдання 7: Знайдіть, який серверний блок відповідає вашому імені хоста

cr0x@server:~$ sudo nginx -T 2>/dev/null | grep -nE 'server_name|listen 443|ssl_certificate'
415:    listen 443 ssl http2;
416:    server_name example.com www.example.com;
432:    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
433:    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
601:    listen 443 ssl;
602:    server_name _;
612:    ssl_certificate /etc/ssl/certs/old-default.pem;
613:    ssl_certificate_key /etc/ssl/private/old-default.key;

Значення: Маєте принаймні два TLS серверні блоки: один правильний, один дефолтний зі старим сертифікатом.

Рішення: Оновіть дефолтний блок (або видаліть його), якщо клієнти можуть потрапити на нього. Також переконайтесь, що кожне ім’я хоста явно вказане в server_name.

Завдання 8: Перевірте, що симвпосилання live/ вказують на найновіші файли

cr0x@server:~$ sudo ls -l /etc/letsencrypt/live/example.com/
total 4
-rw-r--r-- 1 root root 692 Sep  2 10:00 README
lrwxrwxrwx 1 root root  42 Dec 28 09:05 cert.pem -> ../../archive/example.com/cert12.pem
lrwxrwxrwx 1 root root  43 Dec 28 09:05 chain.pem -> ../../archive/example.com/chain12.pem
lrwxrwxrwx 1 root root  47 Dec 28 09:05 fullchain.pem -> ../../archive/example.com/fullchain12.pem
lrwxrwxrwx 1 root root  45 Dec 28 09:05 privkey.pem -> ../../archive/example.com/privkey12.pem

Значення: Nginx повинен посилатися на ці симвпосилання. Certbot обертає цільові файли.

Рішення: Якщо Nginx вказує безпосередньо на archive/, змініть на live/, щоб майбутні оновлення не вимагали ручних змін.

Завдання 9: Перегляньте дати сертифікатів на диску (що саме створило оновлення)

cr0x@server:~$ sudo openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -dates -issuer -subject
notBefore=Dec 28 09:05:11 2025 GMT
notAfter=Mar 28 09:05:10 2026 GMT
issuer=C = US, O = Let's Encrypt, CN = R11
subject=CN = example.com

Значення: Файл на диску новий.

Рішення: Якщо диск новий, а віддається старий — проблема в reload/SNI/TLS-термінаторі, а не в оновленні.

Завдання 10: Перевірте історію оновлень Certbot і чи виконувався deploy hook

cr0x@server:~$ sudo grep -R "Deploying certificate" -n /var/log/letsencrypt/letsencrypt.log | tail -n 3
2025-12-28 09:05:12,345:INFO:Deploying certificate to /etc/letsencrypt/live/example.com/fullchain.pem
2025-12-28 09:05:12,346:INFO:Deploying key to /etc/letsencrypt/live/example.com/privkey.pem
2025-12-28 09:05:12,900:INFO:Running deploy-hook command: systemctl reload nginx

Значення: Certbot оновив сертифікат і намагався виконати reload.

Рішення: Якщо немає запису про deploy-hook, додайте його. Якщо він є — перевірте, чи успішно він пройшов у журналі systemd.

Завдання 11: Перевірте таймер Certbot (планування systemd в Ubuntu 24.04)

cr0x@server:~$ systemctl list-timers --all | grep -E 'certbot|letsencrypt'
Sun 2025-12-29 06:18:00 UTC  10h left  Sat 2025-12-28 06:18:01 UTC  13h ago  snap.certbot.renew.timer  snap.certbot.renew.service

Значення: Оновлення заплановано (тут через unit Snap).

Рішення: Якщо таймера немає — оновлення не відбуватиметься автоматично. Якщо таймер є, але ви використовували apt certbot, може бути дві конкуретуючі установки — оберіть одну.

Завдання 12: Підтвердіть, який Certbot ви використовуєте (Snap vs apt), щоб уникнути роздвоєння

cr0x@server:~$ which certbot
/snap/bin/certbot
cr0x@server:~$ snap list certbot
Name    Version   Rev  Tracking       Publisher  Notes
certbot 2.11.0    3741 latest/stable  certbot*   classic

Значення: Ви використовуєте Snap Certbot.

Рішення: Переконайтесь, що всі хуки, логи та очікування відповідають пакету Snap. Уникайте змішування з apt-встановленням Certbot, якщо вам подобається судова археологія.

Завдання 13: Дізнайтесь, чи стоїте ви за балансувальником або проксі, що завершує TLS

cr0x@server:~$ curl -sI https://example.com | grep -iE 'server:|via:|x-forwarded|cf-|x-amz'
server: cloud-proxy
via: 1.1 edge-gw
x-forwarded-proto: https

Значення: Заголовки натякають на шар проксі. Це не доказ, але сильний запах проблеми.

Рішення: Перевірте DNS і мережевий шлях. Якщо TLS термінується зверху — оновлюйте сертифікат там, а не на Nginx.

Завдання 14: Перевірте журнали помилок Nginx для невдач при читанні сертифікатів або reload

cr0x@server:~$ sudo tail -n 30 /var/log/nginx/error.log
2025/12/28 09:05:13 [notice] 2147#2147: signal process started
2025/12/28 09:05:13 [emerg] 2147#2147: cannot load certificate "/etc/letsencrypt/live/example.com/fullchain.pem": BIO_new_file() failed (SSL: error:80000002:system library::No such file or directory)

Значення: Спроба reload була, але не вдалась прочитати файл. Nginx залишить стару конфігурацію активною.

Рішення: Виправте шлях до файлу, права або обмеження SELinux/AppArmor (AppArmor частіше на Ubuntu). Потім перезавантажте знову.

Завдання 15: Доведіть, який файл сертифікату відкритий у робочому процесі Nginx

cr0x@server:~$ sudo lsof -p 2147 | grep -E 'fullchain|cert\.pem|privkey' | head
nginx 2147 root  mem REG  252,0   4231  131072 /etc/letsencrypt/live/example.com/fullchain.pem
nginx 2147 root  mem REG  252,0   1704  131073 /etc/letsencrypt/live/example.com/privkey.pem

Значення: Це найближче до «що Nginx завантажив» з погляду ОС.

Рішення: Якщо воно все ще вказує на старий файл з archive або інший домен — виправте конфіг і перезавантажте. Якщо там правильно, а клієнти бачать старе — TLS завершується десь іще.

Завдання 16: Підтвердіть, що ваше ім’я хоста резолвиться на машину, яку ви дебажите

cr0x@server:~$ dig +short A example.com
203.0.113.10
cr0x@server:~$ ip -brief address show | awk '{print $1,$3}'
lo 127.0.0.1/8
eth0 203.0.113.10/24

Значення: DNS A-запис відповідає IP хоста.

Рішення: Якщо DNS вказує в інше місце (або ви за anycast/LB), ви дебагуєте не ту машину. Це трапляється частіше, ніж усі признаються.

Три міні-історії з корпоративного життя

Міні-історія 1: Інцидент від неправильної припущення

Середня компанія думала, що має «просту» конфігурацію: одна VM, один Nginx, один домен. Або так усі вірили.
Вони оновили сертифікат Let’s Encrypt на Ubuntu, перезавантажили Nginx, а сповіщення про закінчення терміну все ще надходили.
Інженер у чергуванні припустив, що моніторинг помиляється, бо certbot certificates показував свіжий термін.

Браузер на їхньому ноутбуці теж показував старий сертифікат. Потім стало ще дивніше: колега на іншому провайдері бачив новий.
Ця різниця змусила поставити правильне питання: «Чи ми стоїмо за edge-мережею?»
Виявилось, що маркетинг нещодавно ввімкнув керований CDN/WAF у панелі DNS-провайдера.
TLS завершувався на edge із завантаженим сертифікатом, який ніхто не оновив.

«Неправильне припущення» — це не технічна некомпетентність; це організаційна проблема. Усі думали, що діаграма архітектури минулого року все ще актуальна.
Насправді система еволюціонувала без єдиного pull request, що торкався Nginx.
Оновлення бекенду нічого не дало, бо бекенд більше не був TLS-ендпоінтом.

Виправлення було нудним: оновити сертифікат там, де TLS термінується, задокументувати це і додати зонд, що перевіряє сертифікат із різних мереж.
Запобігання — ще нудніше: щотижневий перегляд змін у налаштуваннях edge.
Ніхто цього не любить, але це краще, ніж дізнатись про зміни з пейджера.

Міні-історія 2: Оптимізація, що зіграла проти

Інша команда хотіла «зменшити кількість reload» на завантаженому флоті Nginx. Вони вірили, що часті reload спричиняють спайки латентності.
Вони відключили автоматичні хуки перезавантаження після оновлення і планували робити reload під час тижневого вікна обслуговування.
Ідея звучала розумно: менше reload, менше рухомих частин.

Через два місяці спрацював прогнозований ефект: оновлення проходило кілька разів, але запущені процеси ніколи не перезавантажувалися.
Деякі сервери були перезапущені з інших причин і віддавали свіжі сертифікати; інші — ні і віддавали застарілі.
Флот перетворився на патчворк з різними датами закінчення. Моніторинг перетворився з чистого сигналу на шум.

Операційний біль виник від неоднозначності. Коли одне ім’я хоста почало падати TLS для підмножини користувачів, інженери не могли зрозуміти, чи це мережева проблема,
окремий вузол чи кеш клієнта. Вони витратили години на збір доказів, що вели до одного висновку: «наша оптимізація створила дрейф стану».

Вони скасували зміни: відновили deploy-хуки, залишили reload автоматичним і виміряли вплив замість припущень.
Правильно виконаний reload Nginx — грейсфул. Він не обриває підключення; запускає нових воркерів і дренує старих.
Проблема не в тому, що reloads небезпечні, а в тому, що операційна узгодженість стала необов’язковою.

Міні-історія 3: Сумна, але правильна практика, що врятувала день

Регульоване підприємство запускало Nginx на Ubuntu зі строгим процесом змін. Усі скаржились на чеклісти.
Але у них була одна практика, яка здавалася бюрократією, а діяла як страховка: перевірка після кожного оновлення.
Після кожного оновлення запускалась завдання ззовні мережі, яке записувало серійний номер і термін поданого сертифікату у центральний лог.

Одного ранку задача помітила один VIP-хост все ще з старим сертифікатом. Все інше було в порядку.
Команда не панікувала; у них був план дій. Перша перевірка: SNI. Друга: сервер за замовчуванням. Третя: слухачі балансувальника.
Вони знайшли застарілий слухач на балансувальнику, що використовував вручну завантажений сертифікат замість керованого.

Виправлення зайняло хвилини, бо доказ був миттєвий і конкретний: «цей слухач подає серійник X; очікували Y».
Жодних суперечок, археології скріншотів чи «в мене працює».
Запит на зміну був швидко затверджений, бо це була проста заміна, а не експериментальна операція.

Мораль — агресивно нефантастична: верифікуйте те, що бачать користувачі, автоматично після кожної ротації.
Це практика, яка робить інженерів недооціненими — до моменту, коли вона рятує від інциденту.

Типові помилки: симптом → корінь → виправлення

1) «Certbot каже, що оновлено, але браузери все ще показують старий термін»

Корінь: Nginx не перезавантажився, або reload зазнав невдачі.

Виправлення: Запустіть sudo nginx -t; потім sudo systemctl reload nginx. Перевірте systemctl status nginx і /var/log/nginx/error.log. Додайте deploy-hook щоб це відбувалось автоматично.

2) «Сертифікат на диску новий, але віддається старий — лише для деяких імен хостів»

Корінь: SNI/серверний блок не той. Дефолтний блок або інше server_name ловить трафік.

Виправлення: Використовуйте openssl s_client -servername для кожного імені хоста. Проаналізуйте nginx -T на предмет listen 443 і server_name. Переконайтесь, що кожне ім’я хоста має правильний сертифікат.

3) «Health checks кажуть, що сертифікат старий, але звичайні користувачі в порядку»

Корінь: Перевірочник здоров’я не надсилає SNI і потрапляє в дефолтний сервер/сертифікат.

Виправлення: Налаштуйте перевірочник щоб використовував ім’я хоста (SNI) або виправте дефолтний сервер на валідний сертифікат для тієї IP-цели.

4) «Reload вдається, але нічого не змінюється»

Корінь: Ви перезавантажили інший Nginx, ніж той, що обслуговує 443, або TLS завершується вгорі.

Виправлення: Підтвердіть власника сокета за допомогою ss -ltnp. Переконайтесь, що DNS вказує на цю машину. Якщо є балансувальник/CDN — оновіть сертифікат там.

5) «Після оновлення Nginx не перезавантажується; він продовжує віддавати старий сертифікат»

Корінь: Нові файли відсутні/нечитаються через права, помилки шляху або неправильні посилання файлів (наприклад, посилання на chain.pem замість fullchain.pem в деяких налаштуваннях).

Виправлення: Перевірте /var/log/nginx/error.log на повідомлення cannot load certificate. Виправте шляхи, права та використовуйте fullchain.pem. Заново запустіть nginx -t.

6) «Лише деякі клієнти бачать старий сертифікат; інші бачать новий»

Корінь: Багато edge-вузлів / декілька A-записів / anycast / пул балансувальника з різними станами.

Виправлення: Розв’язуйте DNS з різних місць; перевірте кожний бекенд окремо; переконайтесь, що оновлення і reload відбуваються скрізь; виправте дрейф.

7) «Ми оновили конфіг Nginx на новий шлях сертифікату, але він потім відкотився»

Корінь: Система управління конфігурацією або плагін Certbot Nginx перезаписав файли.

Виправлення: Керуйте TLS-директивами в єдиному джерелі правди (Ansible, Puppet тощо) і уникайте ручних правок генерованих фрагментів без власності.

Жарт №2: Нічого так не збирає команду, як сертифікат, що закінчується — раптом усі пам’ятають, хто відповідає за DNS.

Контрольні списки / покроковий план

Покроково: безпечно виправити ситуацію «оновлено, але все ще старе»

  1. Підтвердіть симптом зовні. Використайте openssl s_client -servername і зафіксуйте expiry + serial.
  2. Підтвердіть, хто володіє :443. Використайте ss -ltnp. Якщо це не Nginx — зупиніться і перенаправте тикет правильно.
  3. Підтвердіть, що DNS вказує сюди. Використайте dig +short і порівняйте з локальними IP.
  4. Проаналізуйте вибір конфігурації Nginx. Використайте nginx -T, знайдіть правильний server_name і ідентифікуйте ssl_certificate шляхи.
  5. Переконайтесь, що ви посилаєтесь на Let’s Encrypt live/ симвпосилання. Виправте будь-яке пряме використання archive/.
  6. Перевірте конфіг. Запустіть nginx -t. Без валідації — жодного reload.
  7. Перезавантажте Nginx. Використайте systemctl reload nginx (або ваш сервіс-менеджер) і підтвердіть успіх в systemctl status.
  8. Повторно перевірте зовні. Тими ж командами, тим же іменем хоста, підтвердіть зміну серійника/терміну.
  9. Якщо без змін — шукайте upstream-термінацію. Перевірте слухачі LB/CDN і місця, де керуються сертифікати.
  10. Автоматизуйте наступний раз. Додайте deploy-hook і зовнішню перевірку після оновлення.

Контрольний список: як має виглядати «добре» на Ubuntu 24.04

  • Конфіг TLS Nginx посилається на /etc/letsencrypt/live/<name>/fullchain.pem і privkey.pem.
  • Існує лише один pipeline CA (Snap Certbot або apt-інструменти), а не обидва одночасно.
  • systemd-таймер регулярно запускає оновлення, і логи показують виконання deploy-хуків.
  • Reload Nginx перевіряється (nginx -t) і моніториться на помилки.
  • Зовнішній зонд верифікує поданий сертифікат після оновлення і алертує при невідповідності.

Контрольний список: уникайте цих «хитрих» ходів

  • Не вказуйте Nginx на /etc/letsencrypt/archive, якщо не любите ручні ротації.
  • Не «оптимізуйте» шляхом видалення reload-хуків. Дрейф знайде вас.
  • Не припускайте, що VM обслуговує TLS тільки тому, що на ній стоїть Nginx.
  • Не відлажуйте лише за допомогою скриншота браузера. Використовуйте openssl і фіксуйте серійники.

Поширені питання

1) Чому Nginx не підхоплює автоматично оновлені сертифікати?

Тому що Nginx читає сертифікат і ключ при завантаженні конфігурації. Оновлення змінює файли на диску, але не стан у пам’яті Nginx.
Потрібен reload (або рестарт), щоб знову відкрити і розпарсити нові файли.

2) Чи безпечно виконувати systemctl reload nginx в продакшні?

Зазвичай — так. Reload розроблений як грейсфул: нові воркери стартують з новою конфігурацією, старі дренуються.
Все ж запустіть nginx -t перед цим; зламана конфігурація перетворює «безпечне» в «самозавданий інцидент».

3) У чому різниця між fullchain.pem і cert.pem?

cert.pem — це лише leaf-сертифікат. fullchain.pem містить leaf плюс проміжні сертифікати.
Багатьом клієнтам потрібні проміжні для правильної побудови довіри, тому fullchain.pem — стандартний вибір для Nginx.

4) Чому я бачу новий сертифікат на диску, а по мережі — старий?

Або Nginx не перезавантажився, або Nginx вказує на інші файли, ніж ви перевіряли, або TLS-ендпоінт — не Nginx (балансувальник/CDN).
Розглядайте це як проблему маршрутизації/володіння, поки не доведено протилежне.

5) Моніторинг каже «сертифікат скоро закінчиться», а openssl s_client показує свіжий сертифікат. Хто бреше?

Можливо ніхто. Моніторинг може перевіряти інше ім’я хоста, перевіряти без SNI, перевіряти стару IP-адресу або перевіряти інший регіон/edge.
Оновіть перевірку так, щоб вона відправляла SNI і валідувала саме те ім’я хоста, до якого звертаються користувачі.

6) Чи можна просто рестартувати Nginx замість reload?

Можна, але reload зазвичай краща практика: менше переривань і менше несподіваних побічних ефектів.
Restart — грубий інструмент; використовуйте його коли reload заблокований (наприклад, завислі воркери) або коли ваш менеджер процесів цього вимагає.

7) Що робити, якщо я використовую балансувальник, який завершує TLS?

Тоді оновлення бекенду не вплине на те, що бачать користувачі. Поворачайте сертифікат на балансувальнику (або ввімкніть керовані сертифікати там).
Ви все одно можете тримати TLS на бекенді для defense-in-depth, але не плутайте його з публічним сертифікатом.

8) Як запобігти цьому в майбутньому?

Дві речі: (1) deploy-hook, який перезавантажує Nginx після успішного оновлення та (2) зовнішня перевірка, що підтверджує серійник/термін поданого сертифікату.
Автоматизація без перевірки — це просто швидка помилка.

9) Який сигнал я довіряю найбільше під час відладки?

Сертифікат, який справді подається реальному клієнту, виміряний з SNI за допомогою openssl s_client (або еквівалентного зонду) і зафіксований з серійником + терміном.
Все інше — допоміжні докази.

Висновок: наступні кроки щоб уникнути повторення

Ця проблема рідко є «Let’s Encrypt зламався». Майже завжди це «шар обслуговування не перемкнувся». Ваш відлад має це відображати.
Починайте з зовнішньої перевірки, доведіть, що подається, доведіть, хто подає, потім вирівняйте шляхи конфігурації і поведінку перезавантаження.

Операційний принцип добре передано перефразованою ідеєю, часто приписуваною Ronald Coase: якщо довго мучити дані, вони визнаються.
В оперуванні: не мучте логи й припущення. Виміряйте живий ендпоінт, потім рухайтесь назад.

Практичні наступні кроки:

  • Уніфікуйте Nginx на посилання /etc/letsencrypt/live/.../fullchain.pem і privkey.pem, а не archive/.
  • Зробіть nginx -t + systemctl reload nginx пост-оновлювальною дією через deploy-hook.
  • Додайте зовнішній зонд, який записує серійник і термін поданого сертифікату для кожного імені хоста після оновлення.
  • Аудитуйте точки термінації TLS: VM, ingress контейнера, балансувальник, CDN. Прикріпіть власника до кожної з них.
← Попередня
MySQL проти MariaDB: готовність у Kubernetes — перевірки, перезапуски та безпека даних
Наступна →
MySQL проти SQLite: ознаки міграції — коли саме пора оновлюватися

Залишити коментар