Ви запускаєте curl на Ubuntu 24.04, отримуєте SSL_connect, handshake failure або доблесне curl: (35), і пайплайн зупиняється. Хтось каже «це мережа». Хтось інший звинувачує «OpenSSL, який дивний». Тим часом вам просто потрібна команда, яка працює, бо продакшн сам себе не задеплоїть.
Це польовий довідник для такого моменту: швидкі перевірки першими, потім глибша діагностика, коли очевидні речі (час, довіра, SNI) вже не очевидні. Тут є суб’єктивні поради, бо ваш канал інцидентів заслуговує менше теорій і більше рішень.
Швидкий план діагностики (що перевіряти спочатку/далі/в кінці)
Якщо у вас лише п’ять хвилин і вже розсерджений пейджер, робіть це в такому порядку. Ця послідовність оптимізована під «найпоширеніші» та «найруйнівніші» причини на Ubuntu 24.04 у корпоративних мережах.
Спочатку: час + DNS + базова досяжність (дешево, з великим ефектом)
- Перевірте системний час та синхронізацію NTP. Якщо час неправильний — TLS неправильний. З криптографією не торгуються.
- Підтвердіть, що ви резолвите той хост, який думаєте. Неправильна IP‑адреса означає неправильний сертифікат, що виглядає як «помилка TLS».
- Переконайтеся, що ви потрапляєте на правильний порт і що там дійсно TLS. Проксі, балансувальник або service mesh можуть поставити plain HTTP на 443 і назвати це «інновацією».
Другий рівень: ланцюжок довіри й сховище CA (найпоширеніша помилка “вчора працювало”)
- Перевірте, чи існує CA‑бандл і чи актуальний він. На Ubuntu зазвичай це пакет
ca-certificatesплюс згенеровані файли‑бандли. - Перевірте, чи ваша компанія не вставляє проксі для інспекції TLS. Якщо так — «реальний» видавець сертифіката може бути корпоративним коренем, а не Let’s Encrypt / DigiCert тощо.
- Шукайте прострілені/проточені проміжні сертифікати. Вони проявляються як «unable to get local issuer certificate» і псують вам ранок.
Третій рівень: SNI/ALPN/версія TLS (де «в браузері працює» може обманювати)
- Протестуйте SNI явно. Без SNI багато серверів віддають сертифікат за замовчуванням, який не співпадає з вашим хостом.
- Перевірте узгодження ALPN (HTTP/2 проти HTTP/1.1). Деякі проміжні пристрої ламаються на HTTP/2 і рукопотискання переривається.
- Примусово встановіть TLS 1.2 або TLS 1.3, щоб відокремити сумісність. Це діагностичний крок, а не постійне «рішення».
Цитата, яку варто тримати на моніторі:
Werner Vogels (парафраз): «Все постійно ламається; проектуйте й експлуатуйте, виходячи з цього.»
Цікавинки та коротка історія (чому це повторюється)
- SNI виник, бо IP стали цінними. Коли хостинг консолідувався, кілька доменів мусили ділити одну IP, але TLS мав знати, який сертифікат показати. SNI (Server Name Indication) вирішив це, вставивши хостнейм у ClientHello.
- TLS 1.3 змінив форму рукопотискання. Він прибрав застарілі шифри й змінив деталі домовленостей; деякі старі проксі «підтримують TLS», але не реалії TLS 1.3.
- ALPN — чому HTTP/2 зазвичай «просто працює». Клієнт і сервер узгоджують
h2чиhttp/1.1під час TLS; коли ALPN ламається, ви бачите дивні симптоми рукопотискання, а не просто повільні передачі. - «Сховище довіри CA» — це не одна річ. Ubuntu керує системною довірою через
/etc/ssl/certsі згенеровані бандли; додатки інколи постачають власні бандли, що створює приємні невідповідності. - Часто сервери неправильно віддають сертифікатний ланцюжок. Сервери мають надсилати проміжні; багато невірних конфігів покладаються на кеш клієнтів, тому «в браузері працює» може вводити в оману.
- Зсув часу був проблемою TLS з самого початку. Вікна дійсності прості й безжальні: забагато вперед або назад — валідація падає, незалежно від ваших почуттів.
- Let’s Encrypt поширив автоматизацію по всьому простору. Чудово для швидкості опсів. Також чудово для виявлення приладів, що не витримують сучасних ланцюжків о 03:00.
- Коди помилок cURL — це стабільний фольклор. «(35) SSL connect error» — це catch‑all, який ховає десятки різних відмов рукопотискання. Потрібна вербозність, щоб не гадати.
Жарт #1: TLS — як VIP‑список: якщо ваш годинник неправильний, ви або ще не народилися, або вже померли. Жоден із станів не пропустить вас в клуб.
Практична модель TLS‑handshake у curl
Коли curl https://api.example.com падає на Ubuntu 24.04, зазвичай ви не проходите один із чотирьох воріт:
Ворота 1: Ви потрапили на потрібного пірінга
TCP‑з’єднання вдалося і ви спілкуєтеся з тим системою, яку мали на меті. Помилки тут виглядають як таймаути, відмови з’єднання або TLS‑рукопотискання, яке виглядає як нісенітниця, бо ви навіть не потрапили на TLS‑ендпойнт.
Ворота 2: Ви узгодили спільний протокол
Клієнт і сервер мають погодити версію TLS і набір шифрів. У TLS 1.3 семантика списку шифрів відрізняється від TLS 1.2. Проміжні пристрої для інспекції трафіку інколи неправильно обробляють цю домовленість, і ви побачите повідомлення на кшталт protocol_version або handshake_failure.
Ворота 3: Перевірка ідентичності (SNI + сертифікат + SAN + ланцюжок)
Сервер показує сертифікатний ланцюжок. Запитаний хостнейм має співпадати з Subject Alternative Name. Ланцюжок має вести до довіреного кореня у вашому локальному сховищі довіри. Якщо SNI відсутній або неправильний, ви можете отримати зовсім інший сертифікат, і весь подальший процес — це просто клієнт, що робить свою роботу, відмовляючись продовжувати.
Ворота 4: Перевірка валідності по часу
Сертифікат має бути дійсний зараз. Саме тут трохи зламаний NTP стає повним провалом. Сертифікати не пробачають; їм байдуже, що VM прокинувся після паузи і годинник показав вчора.
Операційне правило: Не «виправляйте» збої TLS відключенням перевірки (-k), якщо ви не діагностуєте в безпечному песочниці. У продакшні це еквівалент зняття батарейки з пожежного датчика, бо він голосно пищить.
Практичні завдання: команди, виводи та рішення (12+)
Це реальні завдання, які можна виконати на Ubuntu 24.04. Кожне містить: команду, приклад виводу, що це означає і яке рішення прийняти далі.
Завдання 1: Захопіть реальну помилку curl з повною TLS‑вербозністю
cr0x@server:~$ curl -v https://api.example.com/health
* Trying 203.0.113.10:443...
* Connected to api.example.com (203.0.113.10) port 443 (#0)
* ALPN: curl offers h2,http/1.1
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS alert, handshake failure (552):
* OpenSSL SSL_connect: SSL_ERROR_SSL in connection to api.example.com:443
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SSL in connection to api.example.com:443
Значення: TCP‑з’єднання пройшло; TLS‑домовленість провалилася. Алерт надійшов від пірингу (або проміжного пристрою, що поводиться як піринг).
Рішення: Перейдіть до перевірок протоколу/SNI/CA/часу. Не витрачайте час на «чи досяжний хост» — він досяжний.
Завдання 2: Підтвердьте системний час і стан NTP
cr0x@server:~$ timedatectl
Local time: Mon 2025-12-29 09:18:44 UTC
Universal time: Mon 2025-12-29 09:18:44 UTC
RTC time: Mon 2025-12-29 09:18:43
Time zone: Etc/UTC (UTC, +0000)
System clock synchronized: yes
NTP service: active
RTC in local TZ: no
Значення: Годинник синхронізований. Ймовірно, час не винуватець.
Рішення: Якщо System clock synchronized: no або час неправильний — виправте NTP передусім. Якщо все ок — рухайтеся далі.
Завдання 3: Перевірте джерела NTP (шукаємо «синхронізацію з нісенітницею»)
cr0x@server:~$ chronyc sources -v
.-- Source mode '^' = server, '=' = peer, '#' = local clock.
/ .- Source state '*' = current best, '+' = combined, '-' = not combined.
| / .- RFC 5905 reachability register.
|| .- Last sample was +/- offset between local clock and source.
|| | .- Polling interval. .- Leap status.
|| | | | | | |
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* ntp1.corp.local 2 6 377 22 -153us[-401us] +/- 12ms
^+ ntp2.corp.local 2 6 377 19 +211us[ +19us] +/- 15ms
Значення: Ви синхронізуєтесь із адекватними джерелами з малими зсувами.
Рішення: Якщо бачите локальний джерело (#) або великі зсуви — виправляйте час. Діагностика TLS з поганим часом — це самонанесення шкоди.
Завдання 4: Перевірте, що DNS резолв відповідає очікуванням
cr0x@server:~$ getent ahosts api.example.com
203.0.113.10 STREAM api.example.com
203.0.113.10 DGRAM api.example.com
203.0.113.10 RAW api.example.com
Значення: Ім’я резолвиться в одну IP. Добре — якщо ця IP очікувана для вашого середовища.
Рішення: Якщо IP несподівана (неправильний регіон/VIP), розберіться із split‑horizon DNS, resolv.conf або VPN DNS.
Завдання 5: Переконайтеся, що маршрут не робить нічого дивного
cr0x@server:~$ ip route get 203.0.113.10
203.0.113.10 via 192.0.2.1 dev eth0 src 192.0.2.55 uid 1000
cache
Значення: Трафік іде через очікуваний шлюз.
Рішення: Якщо маршрут іде тунелем або несподіваним інтерфейсом — підозрюйте проксі/VPN і тестуйте з чистого мережевого шляху.
Завдання 6: Підтвердіть, що на порту працює TLS
cr0x@server:~$ timeout 5 openssl s_client -connect api.example.com:443 -brief
40F7E0A5D37F0000:error:0A00010B:SSL routines:ssl3_get_record:wrong version number:../ssl/record/ssl3_record.c:358:
Значення: «Wrong version number» часто означає, що ви потрапили на не‑TLS сервіс (plain HTTP на 443) або на проксі, що очікує CONNECT.
Рішення: Перевірте змінні проксі, слухачі балансувальника та чи потрібно вам використовувати https_proxy з CONNECT.
Завдання 7: Тест SNI явно (тест «правильного сертифіката»)
cr0x@server:~$ openssl s_client -connect 203.0.113.10:443 -servername api.example.com -showcerts </dev/null | head -n 20
CONNECTED(00000003)
---
Certificate chain
0 s:CN = api.example.com
i:C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
-----BEGIN CERTIFICATE-----
MIIF...
-----END CERTIFICATE-----
---
Server certificate
subject=CN = api.example.com
issuer=C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
Значення: З SNI ви отримали сертифікат для api.example.com. Добрий знак.
Рішення: Якщо subject — якийсь сертифікат за замовчуванням (або незв’язаний хост), виправте використання SNI або перевірте, чи ваш DNS/VIP мапиться на правильний бекенд.
Завдання 8: Дізнайтеся, чому валідація падає (ланцюжок, хостнейм, термін дії)
cr0x@server:~$ openssl s_client -connect api.example.com:443 -servername api.example.com -verify_return_error </dev/null
...
depth=0 CN = api.example.com
verify error:num=20:unable to get local issuer certificate
Verification error: unable to get local issuer certificate
Verify return code: 20 (unable to get local issuer certificate)
Значення: Ланцюжок не може бути побудований до довіреного кореня локально. Або ваше сховище CA відсутнє, або проксі підмінює сертифікат корпоративним коренем.
Рішення: Розгляньте видавця і порівняйте з вашим сховищем довіри. Якщо ви в корпоративній мережі — підтвердіть встановлення корпоративного кореня CA.
Завдання 9: Швидко ідентифікуйте видавця та SAN
cr0x@server:~$ echo | openssl s_client -connect api.example.com:443 -servername api.example.com 2>/dev/null | openssl x509 -noout -issuer -subject -dates -ext subjectAltName
issuer=C = US, O = DigiCert Inc, CN = DigiCert TLS RSA SHA256 2020 CA1
subject=CN = api.example.com
notBefore=Dec 1 00:00:00 2025 GMT
notAfter=Mar 1 23:59:59 2026 GMT
X509v3 Subject Alternative Name:
DNS:api.example.com, DNS:api-internal.example.com
Значення: Хостнейм присутній у SAN; вікно дійсності виглядає нормальним.
Рішення: Якщо SAN не включає ваш хостнейм — це проблема на сервері (або ви використовуєте неправильний хостнейм). Виправляйте ендпойнт, а не curl.
Завдання 10: Перевірте CA‑бандл і оновіть його (системна довіра)
cr0x@server:~$ dpkg -l | grep -E '^ii\s+ca-certificates\s'
ii ca-certificates 20240203 all Common CA certificates
Значення: Пакет CA‑бандла встановлений.
Рішення: Якщо відсутній — встановіть. Якщо встановлений, але застарілий — оновіть і згенеруйте заново.
cr0x@server:~$ sudo apt-get update
Hit:1 http://archive.ubuntu.com/ubuntu noble InRelease
Get:2 http://security.ubuntu.com/ubuntu noble-security InRelease [110 kB]
Fetched 110 kB in 1s (168 kB/s)
Reading package lists... Done
cr0x@server:~$ sudo apt-get install --only-upgrade ca-certificates
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
ca-certificates is already the newest version (20240203).
0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
cr0x@server:~$ sudo update-ca-certificates
Updating certificates in /etc/ssl/certs...
0 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
Значення: Системне CA‑сховище консистентне.
Рішення: Якщо сертифікати були додані/видалені — протестуйте curl знову. Якщо все ще падає — підозрюйте невстановлений корпоративний MITM‑сертифікат або використання додаткового бандла в додатку.
Завдання 11: Виявлення налаштувань проксі, що непомітно змінюють рукопотискання
cr0x@server:~$ env | grep -iE 'https?_proxy|no_proxy'
https_proxy=http://proxy.corp.local:3128
http_proxy=http://proxy.corp.local:3128
no_proxy=localhost,127.0.0.1,.corp.local
Значення: curl ймовірно тунелюватиме через проксі, що може провалити CONNECT, перехоплювати TLS або вимагати автентифікації.
Рішення: Якщо ціль має бути досяжна напряму — тимчасово зніміть змінні проксі для тесту або додайте хост у no_proxy.
cr0x@server:~$ curl -v --noproxy api.example.com https://api.example.com/health
* Trying 203.0.113.10:443...
* Connected to api.example.com (203.0.113.10) port 443 (#0)
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
> GET /health HTTP/1.1
< HTTP/1.1 200 OK
ok
Значення: Пряме підключення працює; шлях через проксі — проблема.
Рішення: Виправте конфігурацію проксі (дозвіл CONNECT, автентифікація або корпоративний CA). Не «вирішуйте» відключенням перевірки TLS.
Завдання 12: Примусіть HTTP/1.1, щоб виключити дивності ALPN/HTTP2
cr0x@server:~$ curl -v --http1.1 https://api.example.com/health
* ALPN: curl offers http/1.1
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
> GET /health HTTP/1.1
< HTTP/1.1 200 OK
ok
Значення: HTTP/1.1 працює. Якщо --http2 падає — у вас непереносність HTTP/2 або проблеми з ALPN у шляху (часто проксі або старий балансувальник).
Рішення: Короткостроково: зафіксуйте HTTP/1.1 для цього середовища. Довгостроково: виправте проміжний пристрій або ALPN‑конфігурацію сервера.
Завдання 13: Примусіть версію TLS, щоб ізолювати проблеми сумісності
cr0x@server:~$ curl -v --tlsv1.2 https://api.example.com/health
* TLSv1.2 (OUT), TLS handshake, Client hello (1):
* TLSv1.2 (IN), TLS handshake, Server hello (2):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
< HTTP/1.1 200 OK
ok
Значення: TLS 1.2 працює. Якщо TLS 1.3 падає — щось у шляху не любить TLS 1.3.
Рішення: Якщо ви контролюєте сервер — ввімкніть/пропатчіть TLS 1.3 коректно. Якщо ні — розгляньте тимчасове фіксування TLS 1.2 і відкрийте тикет на виправлення шляху.
Завдання 14: Дізнайтеся точно, який CA‑файл використовує curl
cr0x@server:~$ curl -v https://api.example.com/health 2>&1 | grep -iE 'CAfile|CApath'
* CAfile: /etc/ssl/certs/ca-certificates.crt
* CApath: /etc/ssl/certs
Значення: curl використовує системний CA‑бандл, як очікується.
Рішення: Якщо вказано інше місце (кастомний бандл) — вирівняйте його із системною довірою або оновіть той бандл окремо.
Завдання 15: Перевірте, чи встановлено корпоративний корінь CA (коли є інспекція TLS)
cr0x@server:~$ ls -l /usr/local/share/ca-certificates
total 4
-rw-r--r-- 1 root root 1874 Dec 28 12:10 corp-root-ca.crt
cr0x@server:~$ sudo update-ca-certificates
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
Значення: Ваш корпоративний корінь CA тепер в системній довірі.
Рішення: Протестуйте curl знову. Якщо тепер працює — корінна причина була «ненадаваний довірений MITM», а не «зла інтернет‑лінія».
Завдання 16: Підтвердіть, що сервер повертає повний сертифікаційний ланцюжок
cr0x@server:~$ openssl s_client -connect api.example.com:443 -servername api.example.com -showcerts </dev/null | awk '/BEGIN CERTIFICATE/{c++} END{print c}'
1
Значення: Відправлено лише один сертифікат. Зазвичай це неправильно, якщо лист не підписаний безпосередньо коренем (рідко для публічних сайтів).
Рішення: Виправте конфіг сервера, щоб віддавати проміжні сертифікати. Не «виправляйте» клієнтів, розсилаючи недостаючі проміжні як заплатки.
Жарт #2: Якщо ви коли‑небудь «виправите» TLS, копіюючи випадкові сертифайли з ноутбука колеги — вітаю, ви винайшли розповсюдження шкідливого ПЗ з кращою документацією.
Чеклисти / покроковий план (швидкі виправлення, що працюють)
Чеклист A: Коли curl падає з (35) або «handshake failure»
- Запустіть
curl -vі зафіксуйте перший рядок помилки TLS та будь‑які рядкиCAfile/CApath. - Перевірте час за допомогою
timedatectl. Якщо не синхронізовано — виправте NTP і протестуйте знову. - Перевірте DNS через
getent ahosts. Якщо резолв несподіваний — виправляйте резолвер/VPN/split DNS. - Перевірте проксі вивівши
https_proxy/no_proxy. Тестуйте з--noproxyде потрібно. - Протестуйте SNI за допомогою
openssl s_client -servername. Підтвердіть, що листий сертифікат і SAN містять ваш хостнейм. - Перевірте валідацію з
-verify_return_error. Якщо видавець недовірений — оновіть CA‑сховище або встановіть корпоративний корінь. - Ізолюйте ALPN/TLS версію за допомогою
--http1.1і--tlsv1.2. Якщо примус допомагає — знайдено проблему сумісності.
Чеклист B: Постійні виправлення, які варто віддавати перевагу (замість «curl -k»)
- Час: тримайте NTP увімкненим і моніторьте дрейф на VM, що призупиняються/відновлюються. Виправляйте платформу, а не симптом.
- Довіра: керуйте коренями CA через конфігураційний менеджмент. Якщо потрібен корпоративний корінь — встановіть його одного разу і плануйте ротацію.
- SNI: завжди використовуйте коректні хостнейми; не звертайтеся до IP, якщо не контролюєте
--resolve/--connect-toі не розумієте валідацію сертифіката. - Проксі: явно визначайте
no_proxyдля внутрішніх імен. Не дозволяйте змінним оточення дивувати вас у systemd‑сервісах та CI. - Гігієна серверів: віддавайте повні ланцюжки, використовуйте сучасні шифри та тестуйте OpenSSL з тієї самої клієнтської ОС, що й ваша автоматизація.
Чеклист C: Коли підозрюєте корпоративний проксі для інспекції TLS
- Підтвердіть, що змінні проксі встановлені (або нав’язані політикою мережі).
- Отримайте та огляньте видавця сертифіката у шляху збоїв. Якщо видавець «корпоративний» — ви маєте встановити корпоративний корінь CA на хості.
- Встановіть корпоративний корінь у
/usr/local/share/ca-certificatesі виконайтеupdate-ca-certificates. - Протестуйте curl знову. Якщо працює — оновіть «золоті» образи та CI‑ранери, щоб не відкривати цю проблему знову наступного кварталу.
Типові помилки: симптом → корінна причина → виправлення
1) Симптом: «certificate has expired», але сайт у браузері в порядку
Корінна причина: Системний час неправильний (часто VM після відновлення) або клієнт бачить інший ланцюжок сертифікатів (інтерцепція проксі) порівняно з браузером.
Виправлення: timedatectl + виправлення NTP; потім огляд видавця через openssl s_client. Якщо видавець корпоративний — встановіть корпоративний корінь CA.
2) Симптом: «unable to get local issuer certificate» / «self-signed certificate in certificate chain»
Корінна причина: Відсутній локально корінь/проміжний або проксі для інспекції TLS представляє лист без встановленого корпоративного кореня.
Виправлення: Оновіть ca-certificates; встановіть потрібний корінь у /usr/local/share/ca-certificates; виконайте update-ca-certificates; протестуйте знову.
3) Симптом: «wrong version number» від OpenSSL / curl
Корінна причина: Ви не говорите TLS на TLS‑порт. Поширені випадки: потрапили на HTTP‑ендпойнт на 443, проксі очікує CONNECT або невідповідність слухача балансувальника.
Виправлення: Перевірте змінні проксі; вивчіть curl -v на наявність proxy CONNECT; перевірте конфігурацію серверного слухача; протестуйте прямий шлях з --noproxy.
4) Симптом: Працює з --tlsv1.2, але падає за замовчуванням
Корінна причина: Непереносність TLS 1.3 у сервері/середньому пристрої або баг у пристрої інспекції/IDS.
Виправлення: Ескалюйте до мережевої/безпечної команди для патча або обходу пристрою. Тимчасово: фіксуйте TLS 1.2 для цього середовища з документованим терміном дії.
5) Симптом: Працює з --http1.1, але зазвичай падає
Корінна причина: Неправильне оброблення ALPN/HTTP2 у проксі або балансувальнику.
Виправлення: Примусово використовуйте HTTP/1.1 як тимчасовий захід; виправляйте ALPN у шляху. Не відключайте HTTP/2 глобально без вагомих підстав.
6) Симптом: Рукопотискання падає тільки для одного хостнейму на тій самій IP
Корінна причина: Несумісність SNI або невірний віртуальний хостинг. Без правильного SNI сервер віддає сертифікат за замовчуванням.
Виправлення: Переконайтеся, що клієнти використовують хостнейм, не IP. Перевірте з openssl s_client -servername. За потреби виправте конфіг vhost на сервері.
7) Симптом: Працює на одному хості Ubuntu, але не на іншому
Корінна причина: Різні корені CA встановлені, різне проксі‑оточення, різні збірки OpenSSL/curl або одне з хостів має застаріле CA‑сховище.
Виправлення: Порівняйте curl -V, dpkg -l ca-certificates, змінні проксі та вихід update-ca-certificates. Узгодьте базову конфігурацію.
Три корпоративні міні‑історії (анонімізовано, технічно точно)
Інцидент через хибне припущення: «То ж просто оновлення сертифіката, нічого страшного»
Команда оновила публічний сертифікат на API‑шлюзі пізно в п’ятницю. У тикеті було «лише оновлення», і на on‑call це підтвердили, бо CN залишився тим самим, а CA — авторитетна. Все виглядало рутинним, а smoke‑тести в браузері пройшли.
За хвилини Linux‑автоматизація почала падати: curl‑перевірки здоров’я, завантаження пакетів з приватного репозиторію і внутрішній деплоймент‑тул на libcurl. Помилка була класична: unable to get local issuer certificate. Інженери далися на думку, що CA‑бандл клієнтів застарів. Вони почали масово виконувати apt-get install --reinstall ca-certificates по машинах, наче це вакцина.
Справжня проблема: шлюз був неправильно налаштований і віддавав лише leaf‑сертифікат, без проміжних. Браузери часто кешують проміжні, тому «на моєму лептопі працює». Headless‑клієнти ці проміжні не мали і відмовлялися будувати ланцюжок.
Виправлення було нудним і серверним: оновити конфіг TLS‑шлюзу, щоб віддавати повний ланцюжок. Урок: «оновлення сертифіката» — не просто семантика, це зміна конфігурації, що може зламати подачу ланцюжка. Після інциденту додали перевірку OpenSSL‑базовану в CI, щоб рахувати кількість сертифікатів у ланцюжку перед промоцією.
Оптимізація, що відгукнулась бумерангом: «Заб’ємо HTTP/2 скрізь»
Платформ‑група вирішила стандартизувати HTTP/2 для внутрішніх сервісних викликів. Ідея була непогана: мультиплексування знижує кількість з’єднань, покращує латентність і гармоніює з сучасними TLS‑стеками. Вони змінили спільний curl‑обгортник, який використовують у job‑ах і build‑агентах: за замовчуванням --http2.
Через тиждень один регіон почав бачити інтермітентні TLS‑помилки рукопотискання. Не таймаути — саме рукопотискання падало з алертами. Ретрі іноді допомагали, іноді ні. Всі звинувачували «пакетні втрати», бо так зручно, коли не хочеш вчитись, як працює ALPN.
Винуватцем був проксі‑кластер для TLS‑інспекції і політик. Він нібито підтримував HTTP/2, але мав конфігураційну особливість: деякі бекенди з певним SNI переводилися в legacy‑шлях, який не міг коректно обробити ALPN h2. Коли клієнт пропонував h2, проксі іноді погоджував його, але потім ламав встановлення стріму, що призводило до загального «handshake failure» upstream.
Практична міра — примусово використовувати HTTP/1.1 для постраждалих доменів, не глобально. Довгостроково мережа мала пропатчити і переконфігурувати проксі. Постмортем був простим: «Оновлення протоколу — це продакшн‑зміна. Виводьте її з канарями, метриками і планом відкату.»
Нудна, але правильна практика, що врятувала день: «Золоті образи з керованою довірою»
Інша організація мала змішане середовище: публічний хмарний, on‑prem і регульовані мережі з обов’язковою TLS‑інспекцією. Їх білою плямою були «працює в dev, падає в prod». Вони вирішили зробити нудну річ: один базовий набір коренів CA та проксі‑конфігів, застосований до кожного Ubuntu‑образу і CI‑ранера.
Коли з’явилась Ubuntu 24.04, у них вже був пайплайн, який валідовував TLS‑зв’язки до критичних ендпойнтів через openssl s_client -servername і curl -v з відомими очікуваннями: правильний видавець, правильний SAN і синхронізація часу. Вони також зафіксували місця, де можуть задаватись змінні проксі (systemd drop‑ins для сервісів, явні CI‑налаштування), замість дозволяти випадковим shell‑профілям їх ставити.
Одного ранку команда безпеки повернула корпоративний корінь CA. Така ротація може стати катастрофою, якщо робити її хост‑за‑хостом. У них — ні. Новий CA був заздалегідь підготовлений у образах і розгорнутий через CM перед завершенням ротації, з моніторингом помилок валідації для виявлення відсталих хостів.
Рятунок не був магією; це була звичка. «Нудна» практика — кероване сховище довіри, відтворювані образи і передпольотні TLS‑перевірки — перетворила потенційний інцидент на непомітну подію з одним акуратним записом у changelog.
Поширені запитання
1) Чому curl падає, а браузер працює?
Браузери кешують проміжні, мають власну логіку довіри і можуть використовувати інші налаштування проксі. Curl зазвичай покладається на системне CA‑сховище і змінні оточення. Тестуйте ланцюжок через openssl s_client -showcerts і порахуйте сертифікати; перевірте, який саме видавець ви бачите.
2) Чи прийнятний curl -k?
Як тимчасова діагностика на непроцесному хості, щоб підтвердити «це проблема довіри» — так. Як рішення — ні. Якщо ви вимикаєте перевірку, ви не використовуєте TLS для безпеки — ви використовуєте його заради виду.
3) Як зрозуміти, чи SNI — проблема?
Якщо підключення по IP працює тільки з -servername (OpenSSL) або якщо без SNI ви отримуєте сертифікат за замовчуванням/незв’язаний — це SNI. Зробіть openssl s_client -connect IP:443 -servername hostname і порівняйте з результатом без -servername.
4) Найшвидший спосіб підтвердити корпоративний проксі для TLS‑інспекції?
Перевірте змінні проксі, а потім подивіться на видавця сертифіката. Якщо видавець — ваша компанія (або CA приладу безпеки), ви під інспекцією. Встановіть корпоративний корінь CA в системну довіру.
5) Чому примус TLS 1.2 «виправляє» проблему?
Він нічого не виправляє — просто обходить несумісність. Деякі проміжні пристрої неправильно обробляють TLS 1.3. Примус TLS 1.2 — діагностика і тимчасова міра, поки ви не піднімете шлях.
6) Які файли використовує Ubuntu 24.04 для довірених CA?
Зазвичай /etc/ssl/certs/ca-certificates.crt (бандл) і хешована директорія /etc/ssl/certs. Локальні CA додають у /usr/local/share/ca-certificates і виконують update-ca-certificates.
7) Чи можуть DNS‑проблеми виглядати як TLS‑збої?
Абсолютно. Якщо хост резолвиться в неправильну IP (неправильний VIP, регіон, captive portal, помилковий split DNS), сервер покаже сертифікат, що не відповідає вашому хостнейму, або TLS буде завершуватися по‑іншому. Завжди рано перевіряйте резолюцію та маршрут.
8) Як відлагоджувати, коли проксі вимагає автентифікації?
З curl -v дивіться на відповіді проксі CONNECT (407 Proxy Authentication Required). Налаштуйте проксі‑креденшіали безпечно (не в історії shell) або впевніться, що no_proxy виключає внутрішню ціль.
9) Що робити, якщо сервер не віддає проміжні?
Виправляйте сервер. Віддавати повний ланцюжок — це обов’язок сервера. Клієнтські хаки (розсилка проміжних) створюють непослідовну поведінку і крихку автоматизацію.
10) Чому контейнери падають, коли хост працює?
Контейнери можуть мати інший CA‑бандл, або його взагалі немає, або іншу поведінку синхронізації часу. Переконайтеся, що образ контейнера включає ca-certificates і використовує коректний час хоста (зазвичай це так, якщо не робити екзотику).
Висновок: наступні кроки, які можна зробити сьогодні
Якщо TLS‑рукопотискання curl падають на Ubuntu 24.04, перестаньте трактувати це як загадку. Виконайте швидкий план:
- Підтвердіть синхронізацію часу (
timedatectl,chronyc sources). - Підтвердіть DNS та шлях (
getent ahosts,ip route get). - Перевірте участь проксі (
env | grep -i proxy, потімcurl --noproxy). - Перевірте SNI і ланцюжок (
openssl s_client -servername, огляньте SAN/issuer/dates). - Лише потім ізолюйте протоколні дивності (примус HTTP/1.1, TLS 1.2) і ескалюйте потрібній команді з доказами.
Потім зробіть дорослий follow‑through: стандартизуйте CA‑довіру в образах, тримайте NTP здоровим і ставтесь до «оптимізацій протоколу» як до продакшн‑змін. Ваше майбутнє «я» усе ще буде на виклику. Зробіть йому ніч спокійнішою.