Ubuntu 24.04 : échecs de poignée de main TLS avec curl — checklist de réparation rapide SNI/CA/heure

Cet article vous a aidé ?

Vous lancez curl sur Ubuntu 24.04, il renvoie SSL_connect, handshake failure ou le fameux curl: (35), et votre pipeline s’arrête. Quelqu’un avance « c’est le réseau ». Un autre blâme « OpenSSL qui fait des siennes ». Pendant ce temps, vous avez juste besoin que la commande fonctionne parce que la prod ne va pas se déployer toute seule.

Ceci est le guide de terrain pour ce moment : vérifications rapides d’abord, puis diagnostic approfondi quand l’évident (heure, confiance, SNI) ne l’est plus. C’est volontiers opinionné parce que votre canal d’incident mérite moins de théories et plus de décisions.

Playbook de diagnostic rapide (quoi vérifier d’abord/deuxième/troisième)

Si vous n’avez que cinq minutes et un pager déjà énervé, faites cela dans l’ordre. Cette séquence est optimisée pour les causes « les plus courantes » et « les plus destructrices » sur Ubuntu 24.04 dans les réseaux d’entreprise.

Première étape : Heure + DNS + accessibilité basique (vérifs peu coûteuses, fort rendement)

  1. Vérifiez l’heure système et la synchronisation NTP. Si l’heure est fausse, TLS est faux. Ne négociez pas avec la cryptographie.
  2. Confirmez que vous résolvez le nom d’hôte que vous pensez. Une IP incorrecte signifie un certificat incorrect, qui ressemble à un « échec TLS ».
  3. Confirmez que vous atteignez le bon port et qu’il s’agit bien de TLS. Un proxy, un load balancer ou un service mesh peut mettre du HTTP clair sur le 443 et appeler ça « innovant ».

Deuxième étape : Chaîne de confiance et magasin CA (la panne « ça marchait hier » la plus fréquente)

  1. Vérifiez que le bundle CA existe et est à jour. Sur Ubuntu, c’est généralement le paquet ca-certificates plus les fichiers de bundle générés.
  2. Vérifiez si votre entreprise insère un proxy d’inspection TLS. Dans ce cas, l’émetteur « réel » est la racine de votre entreprise, pas Let’s Encrypt / DigiCert / etc.
  3. Cherchez des intermédiaires expirés. Ils apparaissent comme « unable to get local issuer certificate » et gâchent votre matinée.

Troisième étape : SNI/ALPN/version TLS (là où « ça marche dans le navigateur » vous trompe)

  1. Testez explicitement le SNI. Sans SNI, beaucoup de serveurs renvoient un certificat par défaut qui ne correspond pas à votre nom d’hôte.
  2. Testez la négociation ALPN (HTTP/2 vs HTTP/1.1). Certains middleboxes échouent sur HTTP/2 et vous obtenez une poignée de main qui meurt en vol.
  3. Forcez TLS 1.2 ou TLS 1.3 pour isoler les problèmes de compatibilité. C’est un geste de diagnostic, pas une « solution » permanente.

Une citation à garder à l’écran :

Werner Vogels (idée paraphrasée) : « Tout échoue tout le temps ; concevez et opérez en supposant que cela arrivera. »

Faits intéressants et courte histoire (pourquoi cela revient)

  • SNI existe parce que les IP sont devenues chères. À mesure que l’hébergement s’est consolidé, plusieurs domaines devaient partager une IP, mais TLS avait besoin de savoir quel cert présenter. SNI (Server Name Indication) a résolu cela en mettant le nom d’hôte dans le ClientHello.
  • TLS 1.3 a changé la forme de la poignée de main. Il a supprimé des chiffrements legacy et modifié des détails de négociation ; certains proxies « supportent TLS » mais pas la réalité du trafic TLS 1.3.
  • ALPN est la raison pour laquelle HTTP/2 « marche généralement ». Le client et le serveur s’accordent sur h2 vs http/1.1 pendant la négociation TLS ; quand ALPN casse, on voit des bizarreries de poignée de main, pas seulement des transferts plus lents.
  • Le « magasin de confiance CA » n’est pas une seule entité. Ubuntu gère la confiance système via /etc/ssl/certs et des bundles générés ; certaines applications embarquent leur propre bundle, causant de délicieuses incohérences.
  • Les chaînes de certificats sont souvent « servies » incorrectement. Les serveurs doivent envoyer les intermédiaires ; beaucoup de mauvaises configs comptent sur des clients ayant mis en cache des intermédiaires lors de visites antérieures, d’où le « ça marche dans le navigateur » trompeur.
  • Le décalage d’horloge est un problème TLS depuis le premier jour. Les fenêtres de validité sont simples et impitoyables : trop tôt ou trop tard et la vérification échoue, indépendamment de vos sentiments.
  • Let’s Encrypt a mis l’automatisation au premier plan. Super pour la vélocité ops. Aussi super pour découvrir quels appliances ne peuvent pas gérer des chaînes modernes à 03:00.
  • Les numéros d’erreur cURL sont un folklore stable. « (35) SSL connect error » est un fourre-tout qui cache des dizaines d’échecs de poignée de main distincts. Vous avez besoin de la sortie verbeuse pour arrêter de deviner.

Blague n°1 : TLS, c’est comme une liste VIP — si votre horloge est fausse, vous n’êtes soit pas encore né, soit déjà mort. Aucun des deux ne vous fait entrer dans le club.

Un modèle mental pratique de la poignée de main TLS de curl

Quand curl https://api.example.com échoue sur Ubuntu 24.04, vous échouez généralement à l’un des quatre portails :

Portail 1 : Vous avez atteint le bon pair

La connexion TCP a réussi et vous parlez au système que vous vouliez atteindre. Les échecs ici apparaissent comme des timeouts, des connexions refusées, ou une poignée de main TLS qui ressemble à du charabia parce que vous n’avez pas réellement touché un endpoint TLS.

Portail 2 : Vous avez négocié un protocole commun

Client et serveur doivent s’accorder sur la version TLS et la suite de chiffrement. Avec TLS 1.3, la sémantique de la « liste de suites de chiffrement » diffère de TLS 1.2. Les middleboxes qui font de l’inspection de trafic gèrent parfois mal cette négociation, et vous verrez des alertes comme protocol_version ou handshake_failure.

Portail 3 : Vous avez vérifié l’identité (SNI + certificat + SAN + chaîne)

Le serveur présente une chaîne de certificats. Le nom d’hôte demandé doit correspondre à une entrée Subject Alternative Name. La chaîne doit mener à une racine de confiance dans votre magasin local. Si le SNI est absent ou erroné, vous pouvez obtenir un mauvais certificat entièrement, et tout ce qui suit n’est que le client faisant son travail en refusant d’aller plus loin.

Portail 4 : Vous avez vérifié la validité temporelle

Le certificat doit être valide maintenant. C’est là que NTP légèrement cassé devient une panne complète. Les certificats ne sont pas indulgents ; ils se moquent du fait que la VM a repris après une pause et que l’horloge est revenue à mardi dernier.

Règle opérationnelle : Ne « corrigez » pas les échecs de poignée de main TLS en désactivant la vérification (-k) sauf si vous êtes en train de diagnostiquer dans un bac à sable sûr. En production, c’est l’équivalent sécurité de retirer la pile de la détecteur de fumée parce qu’il beugle.

Tâches pratiques : commandes, sorties et décisions (12+)

Voici des tâches réelles que vous pouvez lancer sur Ubuntu 24.04. Chaque tâche inclut : une commande, une sortie d’exemple, ce que cela signifie, et la décision à prendre ensuite.

Task 1: Capture the real curl error with full TLS verbosity

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

Sens : La connexion TCP a fonctionné ; la négociation TLS a échoué. L’alerte provient du pair (ou d’un middlebox agissant comme pair).

Décision : Passez aux vérifications protocole/SNI/CA/heure. Ne perdez pas de temps à vérifier « l’hôte est-il joignable ». Il l’est.

Task 2: Confirm system time and NTP state

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

Sens : L’horloge est synchronisée. L’heure n’est probablement pas la cause.

Décision : Si System clock synchronized: no ou si l’heure est fausse, corrigez NTP avant tout. Si c’est correct, poursuivez.

Task 3: Check NTP sources (spot “synced to nonsense”)

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

Sens : Vous êtes synchronisé sur des sources saines avec des offsets faibles.

Décision : Si vous voyez une source d’horloge locale (#) ou des offsets importants, corrigez l’heure d’abord. Déboguer TLS avec une mauvaise heure, c’est du masochisme.

Task 4: Validate DNS resolution matches expectation

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

Sens : Le nom se résout en une IP. Très bien — sauf si cette IP est incorrecte pour votre environnement.

Décision : Si cette IP est inattendue (mauvaise région/VIP), investiguez le DNS split-horizon, resolv.conf, ou le push DNS du VPN.

Task 5: Verify the route isn’t doing something surprising

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

Sens : Le trafic passe via votre gateway attendu.

Décision : Si ça route sur un tunnel ou une interface inattendue, suspectez proxys/VPNs et testez depuis un chemin réseau propre.

Task 6: Confirm you’re actually speaking TLS on that port

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:

Sens : « Wrong version number » signifie souvent que vous avez touché un service non-TLS (HTTP clair sur 443) ou un proxy qui attend un CONNECT.

Décision : Vérifiez les variables proxy, les listeners du load balancer, et si vous devez utiliser https_proxy avec CONNECT.

Task 7: Test SNI explicitly (the “right cert” test)

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

Sens : Avec SNI, vous avez reçu un certificat pour api.example.com. Bon signe.

Décision : Si le subject est un certificat par défaut (ou un nom d’hôte non lié), corrigez l’utilisation du SNI ou vérifiez que votre DNS/VIP mappe vers le backend correct.

Task 8: See why verification fails (chain, hostname, expiry)

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)

Sens : La chaîne ne peut pas être construite jusqu’à une racine de confiance locale. Soit votre magasin CA manque l’émetteur, soit un proxy échange les certificats contre une racine d’entreprise non approuvée.

Décision : Inspectez l’émetteur et comparez avec votre magasin de confiance. Si vous êtes en réseau d’entreprise, confirmez l’installation de la racine d’entreprise.

Task 9: Identify the issuer and SANs quickly

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

Sens : Le nom d’hôte est présent dans le SAN ; la fenêtre de validité semble correcte.

Décision : Si le SAN n’inclut pas votre nom d’hôte, c’est un problème côté serveur (ou vous utilisez le mauvais nom d’hôte). Corrigez l’endpoint, pas curl.

Task 10: Verify CA bundle and update it (system trust)

cr0x@server:~$ dpkg -l | grep -E '^ii\s+ca-certificates\s'
ii  ca-certificates  20240203  all  Common CA certificates

Sens : Le paquet du bundle CA est installé.

Décision : S’il manque, installez-le. S’il est installé mais obsolète, mettez-le à jour et régénérez.

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.

Sens : Votre magasin CA système est cohérent.

Décision : Si des certificats ont été ajoutés/supprimés, retestez curl. Si ça échoue toujours, suspectez une CA MITM d’entreprise non installée ou l’utilisation d’un bundle CA spécifique à l’application.

Task 11: Detect proxy settings that silently change the handshake

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

Sens : curl passera probablement par un proxy, qui peut échouer sur CONNECT, intercepter TLS, ou requérir une authentification.

Décision : Si la cible doit être atteinte directement, désactivez les vars proxy pour le test ou ajoutez le nom d’hôte à 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

Sens : Le chemin direct fonctionne ; le chemin via proxy posait problème.

Décision : Corrigez la configuration du proxy (allowlist CONNECT, auth, ou CA d’entreprise). Ne « résolvez » pas en désactivant la vérification TLS.

Task 12: Force HTTP/1.1 to rule out ALPN/HTTP2 weirdness

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

Sens : HTTP/1.1 fonctionne. Si --http2 échoue, vous avez une intolérance HTTP/2 ou ALPN sur le chemin (souvent un proxy ou une config de load balancer obsolète).

Décision : Court terme : pincez vers HTTP/1.1 pour cet environnement. Long terme : corrigez le middlebox ou la config ALPN du serveur.

Task 13: Force TLS version to isolate compatibility issues

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

Sens : TLS 1.2 fonctionne. Si TLS 1.3 échoue, quelque chose sur le chemin est allergique à TLS 1.3.

Décision : Si vous contrôlez le serveur, activez/patch TLS 1.3 correctement. Si vous ne le contrôlez pas, envisagez de pinner TLS 1.2 comme mitigation temporaire et ouvrez le ticket approprié pour corriger le chemin.

Task 14: See exactly which CA file curl uses

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

Sens : curl utilise le bundle CA système comme prévu.

Décision : Si cela pointe ailleurs (bundle personnalisé), alignez-le avec la confiance système ou mettez à jour ce bundle spécifiquement.

Task 15: Validate a corporate root CA is installed (when TLS inspection is present)

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.

Sens : Votre racine CA d’entreprise fait désormais partie de la confiance système.

Décision : Retestez la commande curl en échec. Si ça marche maintenant, la cause racine était « MITM non approuvé », pas « Internet cassé ».

Task 16: Confirm the server returns the full certificate chain

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

Sens : Un seul certificat a été envoyé. C’est généralement incorrect à moins que le leaf soit signé directement par une racine (rare pour les sites publics).

Décision : Corrigez la configuration serveur pour envoyer les intermédiaires. Ne « corrigez » pas les clients en diffusant des intermédiaires manquants comme du contrebande.

Blague n°2 : Si vous « corrigez » TLS en copiant des fichiers de certificats aléatoires depuis l’ordinateur d’un collègue, félicitations — vous avez réinventé la distribution de malware avec une meilleure documentation.

Listes de contrôle / plan pas à pas (correctifs rapides durables)

Checklist A : Quand curl échoue avec (35) ou « handshake failure »

  1. Exécutez curl -v et capturez la première ligne d’erreur TLS et toutes les lignes CAfile/CApath.
  2. Vérifiez l’heure avec timedatectl. Si non synchronisé, corrigez NTP et retestez.
  3. Vérifiez le DNS avec getent ahosts. S’il se résout de façon inattendue, corrigez le résolveur/VPN/DNS split.
  4. Vérifiez les proxys en affichant https_proxy/no_proxy. Retestez en utilisant --noproxy si approprié.
  5. Testez le SNI avec openssl s_client -servername. Confirmez que le certificat leaf et le SAN contiennent votre nom d’hôte.
  6. Vérifiez la validation en utilisant -verify_return_error. Si l’émetteur n’est pas approuvé, mettez à jour le magasin CA ou installez la racine d’entreprise.
  7. Isolez ALPN/version TLS avec --http1.1 et --tlsv1.2. Si forcer fonctionne, vous avez trouvé une incompatibilité.

Checklist B : Correctifs permanents à privilégier (plutôt que « curl -k »)

  • Heure : maintenez NTP actif, et surveillez la dérive sur les VMs qui se suspendent/reprennent. Corrigez la plateforme, pas le symptôme.
  • Confiance : gérez les racines CA via la gestion de configuration. Si vous avez besoin d’une racine d’entreprise, installez-la une fois et faites sa rotation de façon planifiée.
  • SNI : utilisez toujours les noms d’hôte corrects ; ne lancez pas curl par IP à moins de contrôler aussi --resolve/--connect-to et de comprendre la validation des certificats.
  • Proxys : définissez explicitement no_proxy pour les noms internes. Ne laissez pas les variables d’environnement surprendre des services systemd et des jobs CI.
  • Hygiène serveur : servez des chaînes complètes, utilisez des chiffrements modernes, et testez avec OpenSSL depuis le même OS client que celui utilisé par votre automatisation.

Checklist C : Quand vous suspectez un proxy d’inspection TLS en entreprise

  1. Confirmez que les variables proxy sont définies (ou appliquées par politique réseau).
  2. Récupérez et inspectez l’émetteur du certificat dans le chemin en échec. Si l’émetteur semble « d’entreprise », vous devez installer la racine CA d’entreprise sur l’hôte.
  3. Installez la racine CA d’entreprise dans /usr/local/share/ca-certificates et exécutez update-ca-certificates.
  4. Retestez curl. Si ça marche, mettez à jour vos images golden et vos runners CI pour ne pas redécouvrir cela chaque trimestre.

Erreurs courantes : symptôme → cause racine → correction

1) Symptom: “certificate has expired” but the site is fine in a browser

Cause racine : Heure système incorrecte (souvent VM reprise) ou le client voit une chaîne différente (interception proxy) que le navigateur.

Correction : timedatectl + corrigez NTP ; puis inspectez l’émetteur avec openssl s_client. Si émetteur d’entreprise, installez la racine d’entreprise.

2) Symptom: “unable to get local issuer certificate” / “self-signed certificate in certificate chain”

Cause racine : Racine/intermédiaire CA manquante localement, ou proxy d’inspection TLS présentant une feuille émise par l’entreprise sans racine d’entreprise installée.

Correction : Mettez à jour ca-certificates ; installez la racine requise sous /usr/local/share/ca-certificates ; exécutez update-ca-certificates ; retestez.

3) Symptom: “wrong version number” from OpenSSL / curl

Cause racine : Vous ne parlez pas TLS sur un port TLS. Cas courants : toucher un endpoint HTTP sur 443, proxy qui attend CONNECT, ou mismatch du listener du load balancer.

Correction : Vérifiez les vars proxy ; utilisez curl -v et cherchez CONNECT proxy ; vérifiez la config du listener serveur ; testez le chemin direct avec --noproxy.

4) Symptom: Works with --tlsv1.2 but fails with default

Cause racine : Intolérance à TLS 1.3 dans le serveur/middlebox, ou appareil d’inspection/IDS bogué.

Correction : Escaladez vers les équipes réseau/sécurité pour patcher ou contourner l’appareil. Mitigation temporaire : pinner TLS 1.2 pour cet environnement seulement (documentez et mettez une date d’expiration sur la mitigation).

5) Symptom: Works with --http1.1 but fails normally

Cause racine : Mauvaise gestion ALPN/HTTP2 dans le proxy ou le load balancer.

Correction : Forcez HTTP/1.1 côté client comme palliatif ; corrigez le support ALPN sur le chemin. Ne désactivez pas globalement HTTP/2 sauf si vous aimez les régressions lentes que personne ne corrèle.

6) Symptom: Handshake fails only for one hostname on the same IP

Cause racine : Mismatch SNI ou routage de virtual host erroné. Sans SNI correct, le serveur présente le certificat par défaut.

Correction : Assurez-vous que les clients utilisent le nom d’hôte, pas l’IP brute. Confirmez avec openssl s_client -servername. Corrigez la config vhost serveur si nécessaire.

7) Symptom: Works on one Ubuntu host but not another

Cause racine : Racines CA différentes installées, environnement proxy différent, builds OpenSSL/curl différents, ou un hôte avec un magasin CA obsolète.

Correction : Comparez curl -V, dpkg -l ca-certificates, vars proxy, et la sortie de update-ca-certificates. Rendre votre baseline cohérente.

Trois mini-histoires d’entreprise (anonymisées, techniquement exactes)

Incident causé par une mauvaise supposition : « C’est juste un renouvellement de certificat, inoffensif »

Une équipe a fait tourner un certificat public-facing sur une passerelle API tard un vendredi. Le ticket de changement disait « juste renouvellement », et l’astreignant a approuvé parce que le CN restait le même et que la CA était réputée. Tout semblait routinier, et les smoke tests passaient dans un navigateur.

En quelques minutes, la flotte d’automatisation Linux a commencé à échouer : vérifications de santé basées sur curl, récupérations de paquets depuis un dépôt privé, et un outil de déploiement interne utilisant libcurl. L’erreur était classique : unable to get local issuer certificate. Les ingénieurs ont supposé que le bundle CA client était obsolète. Ils ont commencé à exécuter apt-get install --reinstall ca-certificates sur les machines comme s’il s’agissait d’un vaccin.

Le véritable problème : la passerelle était mal configurée pour ne servir que le certificat leaf, pas l’intermédiaire. Les navigateurs avaient souvent mis en cache des intermédiaires, donc « ça marche sur mon laptop ». Les clients headless n’avaient pas cet intermédiaire en cache et ont refusé la chaîne.

La correction fut ennuyeuse et côté serveur : mettre à jour la configuration TLS de la passerelle pour présenter la chaîne complète. La leçon : « renouvellement de certificat » n’est pas un simple label sémantique ; c’est un changement de config qui peut casser la présentation de la chaîne. Après coup, ils ont ajouté un contrôle basé sur OpenSSL du nombre de certificats dans la chaîne dans le CI avant promotion.

Optimisation qui s’est retournée : « Forçons HTTP/2 partout »

Un groupe plateforme a décidé de standardiser HTTP/2 pour les appels internes de service. L’idée n’était pas mauvaise : la multiplexation réduit les connexions, améliore la latence sous charge, et s’intègre bien aux stacks TLS modernes. Ils ont poussé un changement dans un wrapper curl partagé utilisé dans des jobs et agents de build : --http2 par défaut et fête.

En une semaine, une région a commencé à voir des échecs intermittents de poignée de main TLS. Pas des timeouts — des handshakes qui mouraient avec des alertes. Les retries fonctionnaient parfois, parfois non. Tout le monde a blâmé « perte de paquets », parce que c’est ce qu’on dit quand on ne veut pas apprendre comment ALPN fonctionne.

Le coupable était un cluster de proxy faisant interception TLS et enforcement de politique. Il supportait nominalement HTTP/2 mais avait une bizarrerie de config : certaines valeurs SNI back-end déclenchaient un chemin legacy incapable de gérer correctement la sélection ALPN h2. Quand le client proposait h2, le proxy négociait parfois et cassait ensuite l’établissement du stream, reportant un échec générique de handshake en amont.

La mitigation pratique fut de forcer HTTP/1.1 pour les domaines affectés via une configuration par hôte, pas globalement. La correction durable a demandé au réseau de patcher et reconfigurer le proxy. L’action postmortem fut claire : « Les montées de protocole sont des changements production. Déployez-les comme tout autre changement, avec canaries, métriques et rollback. »

Pratique ennuyeuse mais correcte qui a sauvé la mise : « Images golden avec confiance gérée »

Une autre organisation exécutait un environnement mixte : cloud public, on-prem, et quelques réseaux régulés avec inspection TLS obligatoire. Ils avaient déjà été brûlés par « marche en dev, échoue en prod » sur les handshakes. Ils ont donc fait quelque chose d’ennuyeux : une baseline unique pour la confiance CA et la configuration proxy, appliquée à chaque image Ubuntu et runner CI.

Quand Ubuntu 24.04 est arrivé, ils avaient déjà un pipeline qui validait la connectivité TLS vers des endpoints critiques en utilisant openssl s_client -servername et curl -v avec des attentes connues : bon émetteur, bon SAN, et synchronisation horaire système. Ils ont aussi verrouillé où les variables proxy pouvaient être définies (drop-ins systemd pour les services, configuration CI explicite) plutôt que de laisser des profils shell aléatoires faire le bazar.

Un matin, l’équipe sécurité a fait tourner la racine CA d’entreprise utilisée pour l’inspection. Une telle rotation peut être un bain de sang si vous la découvrez hôte par hôte. Ils ne l’ont pas découvert. La nouvelle CA était déjà staged dans les images et déployée via la gestion de configuration avant la rotation, avec monitoring sur les erreurs de vérification de certificat pour attraper les retardataires.

Le sauvetage n’était pas magique ; c’était de l’habitude. La pratique « ennuyeuse » — magasin de confiance géré, images reproductibles, et checks TLS en preflight — a transformé ce qui aurait pu être un incident multi-équipes en un non-événement plus un changelog propre.

FAQ

1) Pourquoi curl échoue mais mon navigateur fonctionne ?

Les navigateurs mettent en cache des intermédiaires, embarquent leur propre logique de confiance, et peuvent utiliser des paramètres proxy différents. Curl dépend typiquement du magasin CA système et des variables d’environnement. Testez la chaîne avec openssl s_client -showcerts et comptez les certificats ; vérifiez l’émetteur que vous voyez réellement.

2) curl -k est-il acceptable ?

Comme diagnostic temporaire sur un hôte non production pour confirmer « c’est un problème de confiance », oui. Comme correctif, non. Si vous devez sauter la vérification, vous n’utilisez pas TLS pour la sécurité — vous l’utilisez pour l’apparence.

3) Comment savoir si SNI est le problème ?

Si se connecter par IP ne fonctionne qu’avec -servername (OpenSSL) ou si vous voyez un certificat par défaut/non lié sans SNI, c’est la réponse. Exécutez openssl s_client -connect IP:443 -servername hostname et comparez avec le résultat sans -servername.

4) Quel est le moyen le plus rapide de confirmer un proxy d’inspection TLS d’entreprise ?

Vérifiez les variables d’environnement proxy, puis inspectez l’émetteur du certificat présenté. Si l’émetteur est votre entreprise (ou une CA d’appliance sécurité) plutôt qu’une CA publique, vous êtes intercepté. Installez la racine CA d’entreprise dans la confiance système.

5) Pourquoi forcer TLS 1.2 « résout » le problème ?

Ça ne le résout pas ; ça contourne l’incompatibilité. Certains middleboxes gèrent mal TLS 1.3. Forcer TLS 1.2 est un diagnostic et un palliatif pendant que vous faites monter le chemin en version.

6) Quels fichiers utilise Ubuntu 24.04 pour les CA de confiance ?

Typiquement /etc/ssl/certs/ca-certificates.crt (bundle) et le répertoire hashé /etc/ssl/certs. Vous ajoutez des CA locales dans /usr/local/share/ca-certificates et exécutez update-ca-certificates.

7) Des problèmes DNS peuvent-ils ressembler à des échecs de poignée de main TLS ?

Absolument. Si vous résolvez un nom vers la mauvaise IP (mauvaise VIP, mauvaise région, portail captif, DNS split mal routé), le serveur présentera un certificat ne correspondant pas à votre nom d’hôte ou termira TLS différemment. Vérifiez toujours la résolution et le routage en premier.

8) Comment déboguer quand un proxy requiert une authentification ?

Avec curl -v, cherchez des réponses CONNECT proxy (407 Proxy Authentication Required). Configurez soit des credentials proxy de façon sécurisée (pas dans l’historique shell), soit assurez-vous que no_proxy exclut la destination interne.

9) Que faire si le serveur n’envoie pas les intermédiaires ?

Corrigez le serveur. Servir une chaîne complète est la responsabilité du serveur. Les hacks côté client (embarquer des intermédiaires) créent des comportements incohérents et une automatisation fragile.

10) Pourquoi les conteneurs échouent quand l’hôte fonctionne ?

Les conteneurs peuvent avoir un bundle CA différent, aucun bundle CA, ou un comportement de synchronisation horaire différent. Assurez-vous que l’image conteneur inclut ca-certificates et qu’elle utilise l’heure correcte de l’hôte (la plupart le font, sauf cas exotiques).

Conclusion : étapes concrètes à faire dès aujourd’hui

Si les handshakes TLS de curl échouent sur Ubuntu 24.04, arrêtez de traiter cela comme un mystère. Exécutez le playbook rapide :

  1. Confirmez la synchro horaire (timedatectl, chronyc sources).
  2. Confirmez le DNS et le chemin (getent ahosts, ip route get).
  3. Vérifiez l’implication d’un proxy (env | grep -i proxy, puis curl --noproxy).
  4. Vérifiez SNI et la chaîne (openssl s_client -servername, inspectez SAN/émetteur/dates).
  5. Ce n’est qu’ensuite que vous isolez les bizarreries de protocole (forcer HTTP/1.1, TLS 1.2) et escaladez la bonne équipe avec des preuves.

Puis faites le suivi adulte : standardisez la confiance CA dans les images, maintenez NTP sain, et traitez les « optimisations » de protocole comme des changements production. Votre futur vous sera encore d’astreinte. Rendez leur nuit plus calme.

← Précédent
ZFS zpool iostat -w : Comprendre les schémas de charge en temps réel
Suivant →
WooCommerce : commandes lentes dans l’administration — détecter les requêtes lourdes et les accélérer

Laisser un commentaire