Vous déployez un registre Docker privé. Il fonctionne pour vous. Il fonctionne pour un runner CI. Puis un nouveau nœud rejoint le cluster et soudain : x509: certificate signed by unknown authority. Les gens commencent à « corriger » le problème en activant insecure-registries parce qu’il est 2 h du matin et que l’alerte sonne.
Ne le faites pas. Les erreurs TLS autour des registries sont un de ces problèmes où la bonne correction est ennuyeuse, répétable et bien moins coûteuse que le bricolage héroïque. La mauvaise correction est rapide, fragile, et revient comme une mauvaise suite — généralement pendant un gel des déploiements.
Ce que signifient réellement les erreurs TLS du registre Docker
La plupart des « erreurs TLS du registre Docker » ne sont pas des problèmes Docker. Ce sont des échecs de validation TLS standard qui remontent via la pile cliente de Docker (la bibliothèque TLS de Go), le démon et parfois containerd. Votre registre n’est qu’un serveur HTTPS avec des contraintes.
Quand vous exécutez :
docker login registry.example.comdocker pull registry.example.com/team/app:tagkubectl applyet que des nœuds commencent à récupérer des images
…le client attend que le serveur présente un certificat qui :
- Correspond au nom d’hôte que vous avez utilisé (Subject Alternative Name, pas seulement le CN).
- Est actuellement valide (non expiré, dans l’intervalle NotBefore/NotAfter).
- S’enchaîne jusqu’à une autorité racine de confiance sur la machine effectuant la récupération.
- Inclut les intermédiaires requis (ou peut les récupérer via AIA, ce qui est peu fiable dans des réseaux restreints).
- Utilise des tailles de clés et algorithmes de signature acceptables.
Les messages d’erreur de Docker sont souvent courts et peu romantiques, comme :
x509: certificate signed by unknown authorityx509: certificate is valid for ..., not registry.example.comremote error: tls: bad certificatetls: handshake failure
Chacun d’eux correspond à un mode d’échec spécifique. Vous pouvez le diagnostiquer avec des étapes déterministes. Vous n’avez pas besoin de « tenter des choses » en production.
Blague n°1 : TLS, c’est comme un videur — si votre chaîne de confiance semble fausse, vous n’entrerez pas dans le club, peu importe à quel point vous insistez que vous êtes « sur la liste ».
Faits et contexte à utiliser dans un postmortem
Un peu de contexte aide, car les échecs TLS proviennent souvent de l’histoire — anciens comportements par défaut, vieilles habitudes, vieille infrastructure.
- Docker a tôt déplacé les décisions de confiance vers le démon. Le démon effectue les pulls, donc son magasin de confiance compte plus que celui de votre shell.
- Subject Alternative Name a remplacé la correspondance CN depuis des années. Les clients modernes ignorent l’ancien Common Name pour la validation d’hôte ; SAN est obligatoire.
- Les autorités intermédiaires sont devenues courantes parce que les racines sont strictement contrôlées. Beaucoup de PKI publiques et privées émettent des certificats finaux depuis des intermédiaires, pas directement depuis des racines.
- La récupération AIA existe mais n’est pas garantie. Certaines piles TLS peuvent télécharger des intermédiaires manquants via Authority Information Access, mais le comportement de Docker dépend de l’environnement et de l’accès réseau.
- Let’s Encrypt a fait évoluer le comportement des chaînes au fil du temps. La sélection de chaîne et les rotations d’intermédiaires ont provoqué des surprises « ça marchait hier » quand les serveurs servaient la mauvaise chaîne.
- Les anciens clients échouent sur les nouveaux algorithmes et inversement. Un registre avec uniquement des certificats ECDSA peut coincer des anciennes versions d’OpenSSL ; RSA seul peut être plus compatible.
- L’inspection TLS en entreprise casse des hypothèses. Les proxies transparents qui ré-signent les certificats font du magasin de confiance interne une dépendance, que vous l’aimiez ou non.
- Les runtimes de conteneurs ont évolué. Sur de nombreux systèmes Docker utilise containerd en interne ; Kubernetes peut utiliser containerd directement. Les chemins des stores de confiance et les mécanismes de rechargement diffèrent.
- « Insecure registries » était prévu pour le développement, pas la production. Cela désactive une vérification critique. Ce n’est pas un « correctif temporaire » ; c’est une décision de politique avec une dette sécuritaire.
Une citation opérationnelle pour vous garder honnête : « L’espoir n’est pas une stratégie. » — Général Gordon R. Sullivan. Cela s’applique douloureusement bien aux chaînes de certificats.
Mode opératoire de diagnostic rapide
Si vous êtes en astreinte, vous ne voulez pas de sermon. Vous voulez un chemin rapide du symptôme à la cause racine.
Première étape : confirmer quel endpoint le client atteint réellement
- S’agit-il de
registry.example.comou d’un nom de load balancer ? - Utilisez-vous un port comme
:5000? - Y a-t-il un proxy ou une inspection TLS MITM ?
Si le nom d’hôte diffère de ce que le certificat couvre, le problème est là : corrigez le DNS ou les SAN.
Deuxième étape : inspecter ce que le serveur présente (chaîne + SAN)
Utilisez openssl s_client contre l’endpoint du registre depuis la machine défaillante. Ne l’inspectez pas depuis votre laptop en supposant que le chemin est identique.
Si le serveur ne présente que le certificat leaf (intermédiaires manquants), corrigez la configuration du serveur. N’« apprenez pas à chaque nœud » des intermédiaires manquants si le registre est celui qui se comporte mal.
Troisième étape : déterminer où la confiance manque
- Si
curléchoue et Docker échoue : le store de confiance de la machine manque la CA ou la chaîne est incorrecte. - Si
curlréussit mais Docker échoue : la configuration de confiance du démon Docker diffère, ou vous utilisez containerd avec sa propre configuration de confiance. - Si seules les récupérations Kubernetes échouent : la confiance du runtime des nœuds diffère de votre machine interactive.
Quatrième étape : vérifier l’heure, car le temps ruine tout
Un décalage d’horloge fait paraître des certificats valides comme invalides. Des pannes NTP provoquent des « erreurs TLS aléatoires » qui ressemblent à des problèmes PKI.
Cinquième étape : résistez à l’option « insecure-registries » sauf si vous acceptez explicitement le risque
Elle vous donnera des builds verts. Elle normalisera aussi la contournement de la vérification, ce qui fait que vous finirez par expédier des identifiants vers le mauvais endpoint plus tard.
Modèle mental de la chaîne de certificats (pour arrêter de deviner)
Une chaîne de certificats TLS est une histoire que le serveur raconte au client : « Je suis registry.example.com, et voici comment vous pouvez me croire. » L’histoire a des personnages :
- Certificat leaf : émis pour le(s) nom(s) d’hôte de votre registre. C’est ce que votre serveur « est ».
- Certificat(s) intermédiaire(s) : l’AC qui a émis le leaf. Ils relient le leaf à une racine.
- Certificat racine : l’ancre de confiance. C’est ce que le client connaît déjà et en qui il fait confiance.
Les clients font généralement confiance aux racines, pas aux intermédiaires. Les intermédiaires ne sont typiquement pas dans le store de confiance de l’OS sauf s’ils sont fournis par le vendeur CA. C’est pourquoi les serveurs doivent souvent présenter la chaîne d’intermédiaires.
Ce que « corriger la chaîne » signifie vraiment
Ça signifie : votre endpoint de registre doit présenter le certificat leaf et tous les intermédiaires requis dans le bon ordre, et le client doit posséder un ancre de confiance (racine) qui valide cette chaîne.
Sur les serveurs web typiques, c’est la différence entre servir :
- Mauvais :
cert.pem(leaf seulement) - Correct :
fullchain.pem(leaf + intermédiaire(s))
De plus : votre CA racine privée doit être installée sur chaque nœud qui récupère des images, y compris les runners CI éphémères et les nœuds Kubernetes autoscalés. Si vous émettez depuis une racine privée qui n’est pas dans la confiance système, rien d’autre n’a d’importance.
Particularité Docker : emplacement de confiance et comportement de rechargement
Docker ne consomme pas automatiquement chaque certificat que vous ajoutez au système. Le démon utilise la confiance système sur de nombreuses distributions, mais Docker supporte aussi des bundles de confiance par registre à :
/etc/docker/certs.d/registry.example.com:5000/ca.crt
Et containerd a ses propres réglages selon la distribution et la distribution Kubernetes. Traduction : vous devez savoir quel runtime effectue le pull et où il lit les CA.
Tâches pratiques : commandes, sorties, décisions (12+)
Voici les tâches que j’exécute réellement lorsque les pulls du registre échouent. Chacune inclut la commande, ce que la sortie typique signifie, et la décision à prendre. Exécutez-les depuis la machine qui échoue (nœud, runner, agent de build), pas depuis la machine que vous voudriez voir échouer.
Task 1: Confirm the exact registry host:port Docker is using
cr0x@server:~$ docker image pull registry.example.com:5000/team/app:1.2.3
Error response from daemon: Get "https://registry.example.com:5000/v2/": x509: certificate signed by unknown authority
Ce que cela signifie : Docker utilise https://registry.example.com:5000 et échoue la vérification de confiance.
Décision : Toutes les vérifications suivantes doivent cibler registry.example.com:5000, y compris les SAN et le chemin du bundle de confiance sous certs.d.
Task 2: Check basic DNS and routing (yes, really)
cr0x@server:~$ getent hosts registry.example.com
10.20.30.40 registry.example.com
Ce que cela signifie : Le nom se résout en 10.20.30.40.
Décision : Si cela diffère selon les nœuds (DNS à horizon partagé), vous pouvez atteindre des load balancers différents avec des certificats différents.
Task 3: Inspect the presented certificate chain and SANs
cr0x@server:~$ echo | openssl s_client -connect registry.example.com:5000 -servername registry.example.com -showcerts 2>/dev/null | openssl x509 -noout -subject -issuer -dates -ext subjectAltName
subject=CN = registry.example.com
issuer=CN = Corp Issuing CA 01
notBefore=Dec 1 00:00:00 2025 GMT
notAfter=Dec 1 23:59:59 2026 GMT
X509v3 Subject Alternative Name:
DNS:registry.example.com, DNS:registry
Ce que cela signifie : Le certificat leaf couvre registry.example.com ; c’est bon. L’émetteur est un intermédiaire (« Corp Issuing CA 01 »).
Décision : Si les SAN n’incluent pas précisément le nom d’hôte utilisé par les clients, réémettez le certificat. Ne comptez pas sur le CN. Si les dates sont incorrectes, corrigez la rotation ou l’horloge.
Task 4: See whether the server is sending intermediates
cr0x@server:~$ echo | openssl s_client -connect registry.example.com:5000 -servername registry.example.com -showcerts 2>/dev/null | awk '/BEGIN CERTIFICATE/{i++} END{print i}'
1
Ce que cela signifie : Un seul certificat (le leaf) est envoyé. C’est une mauvaise configuration classique si les clients n’ont pas déjà l’intermédiaire.
Décision : Corrigez la configuration TLS du registre pour servir la full chain (leaf + intermédiaire(s)). C’est généralement l’endroit correct pour résoudre le problème.
Task 5: Verify the chain locally with a specific CA bundle
cr0x@server:~$ openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt /tmp/registry-leaf.pem
CN = registry.example.com
error 20 at 0 depth lookup: unable to get local issuer certificate
error /tmp/registry-leaf.pem: verification failed
Ce que cela signifie : Le store de confiance système ne peut pas construire la chaîne du leaf jusqu’à une racine de confiance (intermédiaire et/ou racine manquant).
Décision : Soit installez la racine corporative correcte dans le store de confiance système, soit corrigez le serveur pour présenter les intermédiaires (ou les deux, selon la conception PKI).
Task 6: Check OS trust store knows your corporate root
cr0x@server:~$ sudo grep -R --line-number "Corp Root CA" /etc/ssl/certs 2>/dev/null | head
/etc/ssl/certs/Corp_Root_CA.pem:1:-----BEGIN CERTIFICATE-----
Ce que cela signifie : La racine corporate est présente dans le répertoire de confiance système (au moins sur cet hôte).
Décision : Si elle est présente mais que la vérification échoue toujours, il vous manque des intermédiaires côté serveur ou vous ne chaînez pas réellement vers cette racine.
Task 7: Test with curl to isolate “Docker vs system” trust
cr0x@server:~$ curl -vkI https://registry.example.com:5000/v2/
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* SSL certificate problem: unable to get local issuer certificate
curl: (60) SSL certificate problem: unable to get local issuer certificate
Ce que cela signifie : Ce n’est pas Docker qui fait quelque chose de spécial ; l’hôte lui-même ne peut pas valider la chaîne du serveur.
Décision : Corrigez d’abord la chaîne/la confiance. Ne touchez pas encore à la configuration Docker.
Task 8: Confirm Docker daemon sees any per-registry CA bundle
cr0x@server:~$ ls -la /etc/docker/certs.d/registry.example.com:5000/
total 12
drwxr-xr-x 2 root root 4096 Jan 3 09:12 .
drwxr-xr-x 3 root root 4096 Jan 3 09:12 ..
-rw-r--r-- 1 root root 1984 Jan 3 09:12 ca.crt
Ce que cela signifie : Docker a un fichier CA par registre disponible.
Décision : Si ce fichier manque sur les nœuds en échec, installez-le (ou mieux, corrigez la chaîne du serveur et distribuez uniquement votre CA racine via la gestion de confiance système standard).
Task 9: Restart Docker after changing trust (because it won’t read your mind)
cr0x@server:~$ sudo systemctl restart docker
cr0x@server:~$ sudo systemctl is-active docker
active
Ce que cela signifie : Docker a redémarré proprement.
Décision : Si le redémarrage échoue, résolvez les problèmes de configuration du démon avant de retester les pulls. Les changements de confiance qui ne sont pas chargés ne comptent pas.
Task 10: Inspect Docker daemon configuration for insecure shortcuts
cr0x@server:~$ cat /etc/docker/daemon.json
{
"log-driver": "json-file",
"insecure-registries": ["registry.example.com:5000"]
}
Ce que cela signifie : Quelqu’un a contourné la vérification TLS pour ce registre.
Décision : Supprimez-le une fois que le TLS approprié est corrigé. Si vous le laissez, vous acceptez le risque MITM pour tous les pulls depuis cet endpoint.
Task 11: Verify what certificate file your registry endpoint is actually serving (Nginx example)
cr0x@server:~$ sudo nginx -T 2>/dev/null | grep -n "ssl_certificate"
57: ssl_certificate /etc/nginx/certs/registry.crt;
58: ssl_certificate_key /etc/nginx/certs/registry.key;
Ce que cela signifie : Nginx sert registry.crt. Cela peut être leaf-only.
Décision : Assurez-vous que ssl_certificate pointe vers un PEM de chaîne complète (leaf + intermédiaires). Puis rechargez Nginx.
Task 12: Confirm the PEM file actually contains a full chain
cr0x@server:~$ awk '/BEGIN CERTIFICATE/{i++} END{print i}' /etc/nginx/certs/registry.crt
2
Ce que cela signifie : Deux certificats dans le PEM (probablement leaf + intermédiaire). C’est généralement ce que vous voulez.
Décision : Si le résultat est 1, vous servez probablement seulement le leaf. Corrigez le fichier et rechargez le serveur.
Task 13: Reload the registry frontend safely (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
cr0x@server:~$ sudo systemctl reload nginx
cr0x@server:~$ sudo systemctl is-active nginx
active
Ce que cela signifie : La config est valide et le rechargement a réussi. Les connexions existantes devraient survivre ; les nouvelles négociations utiliseront les nouveaux certificats.
Décision : Préférez le rechargement au redémarrage durant les heures ouvrables.
Task 14: Re-test chain presentation after server change
cr0x@server:~$ echo | openssl s_client -connect registry.example.com:5000 -servername registry.example.com -showcerts 2>/dev/null | awk '/BEGIN CERTIFICATE/{i++} END{print i}'
2
Ce que cela signifie : Le serveur envoie maintenant plus que le seul certificat leaf.
Décision : Retestez avec curl puis docker pull. Si cela échoue encore, l’ancre de confiance peut manquer.
Task 15: Validate with curl again (now it should succeed)
cr0x@server:~$ curl -sSI https://registry.example.com:5000/v2/ | head -n 5
HTTP/1.1 401 Unauthorized
Server: nginx
Docker-Distribution-Api-Version: registry/2.0
Www-Authenticate: Bearer realm="https://registry.example.com:5000/token",service="registry.example.com:5000"
Date: Fri, 03 Jan 2026 09:27:31 GMT
Ce que cela signifie : Le TLS a réussi ; l’auth HTTP est maintenant le « problème », ce qui est normal. Le registre attend des identifiants.
Décision : Passez aux tests Docker login/pull. Votre couche TLS est maintenant saine.
Task 16: Confirm Docker can now talk TLS to /v2/
cr0x@server:~$ docker login registry.example.com:5000
Username: cr0x
Password:
Login Succeeded
Ce que cela signifie : La validation TLS et l’authentification ont toutes deux fonctionné.
Décision : Déployez la même correction sur tous les nœuds/runners. Ensuite, supprimez toutes les configurations d’insecure registry.
Task 17: Check node time if cert validity looks wrong
cr0x@server:~$ timedatectl status | sed -n '1,8p'
Local time: Fri 2026-01-03 09:28:02 UTC
Universal time: Fri 2026-01-03 09:28:02 UTC
RTC time: Fri 2026-01-03 09:28:03
Time zone: Etc/UTC (UTC, +0000)
System clock synchronized: yes
NTP service: active
Ce que cela signifie : L’horloge est synchronisée.
Décision : Si elle n’est pas synchronisée, corrigez NTP avant d’accuser la PKI. Les erreurs expired/not-yet-valid viennent souvent d’une dérive temporelle.
Trois mini-récits d’entreprise issus du terrain
1) L’incident causé par une fausse hypothèse : « Le load balancer gère la chaîne »
Une entreprise de taille moyenne faisait tourner un registre privé derrière un load balancer de couche 7. L’équipe plateforme renouvelait les certificats trimestriellement. Ils supposaient que le load balancer servait la chaîne complète parce que les navigateurs étaient satisfaits. Les navigateurs, bien sûr, sont indulgents : ils mettent en cache les intermédiaires, les récupèrent via AIA, ils « marchent » jusqu’à ce qu’ils ne marchent plus.
Puis ils ont déployé de nouveaux nœuds workers Linux dans un pool autoscalé. Machines fraîches, image de base minimale, egress verrouillé. Ces nœuds ne pouvaient pas récupérer les intermédiaires depuis Internet public, et ils n’avaient pas l’intermédiaire en cache parce qu’ils étaient nés récemment.
Le mode d’échec était délicieusement répétitif : chaque nouveau nœud échouait les pulls d’images avec x509: certificate signed by unknown authority. Les anciens nœuds continuaient à fonctionner parce que leurs caches de confiance et le comportement historique masquaient le défaut. L’incident a été mal interprété comme « l’autoscaling est cassé » et « containerd est instable ». Ce n’était pas le cas. La chaîne était incomplète.
La correction a été embarrassante de simplicité : configurer le load balancer pour présenter la chaîne complète (leaf + intermédiaire), et valider en utilisant openssl s_client depuis un nœud neuf sans état caché. La vraie leçon : les navigateurs ne sont pas un test de conformité pour vos clients d’infrastructure.
2) L’optimisation qui s’est retournée contre eux : « Nous n’utiliserons que ECDSA »
Une autre organisation a décidé de moderniser TLS. Ils ont généré des certificats ECDSA parce qu’ils sont plus rapides et plus petits, et ont durci l’entrée du registre pour favoriser des chiffrements modernes. Sur le papier, c’était un changement propre et une belle diapositive en revue sécurité.
Puis une flotte de build legacy a commencé à échouer. Certains runners avaient une vieille pile OpenSSL compilée sans le support de la bonne courbe ; quelques appliances fournisseurs faisaient la terminaison TLS et la ré-encryption avec un support d’algorithmes limité. Le registre n’était pas « down », mais il était sélectivement inaccessible.
La première réaction de l’équipe a été de revenir en arrière. La seconde réaction a été meilleure : déployer une stratégie dual-stack de certificats — supporter RSA et ECDSA lorsque possible, ou au minimum confirmer que tous les clients dans le périmètre peuvent négocier les algorithmes choisis. Ils ont aussi appris à tester avec la flotte réelle, pas un laptop moderne unique.
Le problème n’était pas qu’ECDSA soit mauvais. Le problème était de croire que « notre infrastructure » est homogène. Elle l’est rarement.
3) La pratique ennuyeuse mais correcte qui a sauvé la mise : « Traitez la distribution des CA comme un artefact de release »
Une équipe fintech faisait tourner plusieurs services internes utilisant une PKI privée, y compris leur registre de conteneurs. Ils avaient une règle : la CA racine corporate et les intermédiaires requis étaient empaquetés et distribués comme n’importe quelle autre dépendance — versionnés, avec checksum, et déployés via leur outil de gestion de configuration.
Quand ils ont roté leur CA intermédiaire (planifié, annoncé, répété), ils ont d’abord mis à jour le paquet « trust bundle ». Les nœuds l’ont reçu progressivement. Ce n’est qu’après que la flotte ait signalé conformité qu’ils ont commencé à émettre de nouveaux certificats leaf pour des endpoints comme le registre.
Le jour de la rotation a été anticlimactique. Quelques retardataires ont échoué les pulls — prévisible pour des serveurs non gérés — et ils ont été corrigés avec l’outillage standard. Pas d’incident à minuit. Pas de réglage d’urgence d’insecure registry. Pas de « pourquoi un seul cluster est cassé ? » mystérieux.
C’était ennuyeux. L’ennui est ce que vous voulez. Blague n°2 : si la rotation de vos certificats est excitante, vous faites du théâtre live, pas des opérations.
Erreurs courantes : symptômes → cause racine → correction
1) Symptom: x509: certificate signed by unknown authority
Cause racine : Le client ne peut pas établir une chaîne vers une racine de confiance. Soit la CA racine n’est pas installée, soit le serveur n’envoie pas les intermédiaires, soit les deux.
Correction : Préférez corriger la présentation de la chaîne côté serveur (servir fullchain). Assurez-vous aussi que la CA racine correcte est installée dans la confiance OS et/ou dans /etc/docker/certs.d/ pour ce registre.
2) Symptom: x509: certificate is valid for foo, not registry.example.com
Cause racine : Mauvais SAN. Arrive souvent quand on émet des certificats pour un nom interne mais que les clients utilisent un nom externe, ou quand le nom du load balancer diffère du nom du registre.
Correction : Réémettez le certificat leaf avec des SAN pour chaque nom utilisé par les clients. Ne colmatez pas avec des hacks DNS sauf si vous contrôlez tous les appelants.
3) Symptom: Works in browser, fails in Docker
Cause racine : Le navigateur a récupéré les intermédiaires via AIA ou avait des intermédiaires en cache ; l’hôte Docker ne les avait pas. Ou la confiance du démon Docker diffère du store de l’utilisateur.
Correction : Validez avec openssl s_client -showcerts depuis le nœud en échec. Servez la chaîne complète sur le serveur. Confirmez le bundle de confiance Docker.
4) Symptom: Some nodes can pull, new nodes cannot
Cause racine : Intermédiaires en cache, images de base inconsistantes, ou distribution CA hétérogène. L’autoscaling révèle la dérive.
Correction : Standardisez l’installation des CA dans l’image ou le bootstrap. Testez sur un nœud propre sans historique TLS.
5) Symptom: tls: handshake failure with no other clues
Cause racine : Incompatibilité de protocole/cipher, problème SNI, ou un proxy au milieu. Parfois le registre parle TLS seulement sur un port et vous touchez un autre.
Correction : Utilisez openssl s_client avec -servername. Confirmez les versions TLS et les chiffrements côté serveur et clients. Identifiez tout proxy interceptant.
6) Symptom: remote error: tls: bad certificate on client
Cause racine : Le serveur a rejeté le côté client du handshake. Cela peut arriver avec une mauvaise configuration mTLS (certificats clients requis), ou avec une chaîne serveur cassée qui embrouille la pile côté serveur.
Correction : Consultez les logs serveur (nginx, envoy, registry). Confirmez si des certificats clients sont requis. Si oui, configurez Docker/client pour mTLS ou désactivez l’exigence pour les endpoints du registre.
7) Symptom: Pull fails only in Kubernetes, not on a bastion
Cause racine : Le store de confiance du runtime node manque la CA, ou les nœuds utilisent containerd directement et ne lisent pas le certs.d de Docker. Ou le pull passe par un proxy interne sur les nœuds.
Correction : Debuguez sur le nœud. Installez la CA là où le runtime l’attend. Pour containerd, utilisez ses mécanismes de configuration de certificats (varie selon la distribution). Évitez de supposer que « les réglages Docker s’appliquent ».
8) Symptom: Suddenly fails after cert rotation
Cause racine : Nouvel intermédiaire introduit, ordre de chaîne incorrect, intermédiaire expiré toujours servi, ou leaf émis par une nouvelle CA non encore approuvée par les clients.
Correction : Servez la bonne full chain. Pré-distribuez les nouvelles racines/intermédiaires avant de tourner les leafs. Validez depuis un client propre.
Listes de contrôle / plan étape par étape
Étape par étape : corriger la chaîne de certificats d’un registre privé (la manière saine)
- Inventoriez les appelants. Listez chaque système qui effectue des pulls : laptops développeurs, runners CI, nœuds Kubernetes, buildeurs en air-gapped, boîtes edge.
- Identifiez le point de terminaison TLS. Est-ce le registre lui-même, Nginx, Envoy/Ingress, ou un load balancer ?
- Confirmez le(s) nom(s) d’hôte utilisés par les clients. Incluez les variantes de port. Incluez les noms DNS internes.
- Émettez un certificat leaf avec les SAN corrects. Ne négociez pas avec la réalité ; si les clients utilisent trois noms, le SAN doit contenir trois noms.
- Construisez un PEM fullchain. Concaténez le leaf puis les intermédiaires (sans la racine sauf si votre plateforme l’attend spécifiquement ; beaucoup ne l’attendent pas).
- Configurez le point TLS pour servir la fullchain. Nginx/Envoy/Ingress doivent présenter les intermédiaires.
- Validez la chaîne depuis un hôte propre. Utilisez
openssl s_client -showcertsetcurl. - Distribuez correctement les ancres de confiance. Installez la CA racine corporate dans la confiance OS de chaque nœud capable de pull ; ajoutez au besoin des bundles CA par registre.
- Rechargez les services. Rechargez Nginx/Ingress. Redémarrez Docker/containerd si nécessaire pour appliquer les mises à jour de confiance.
- Supprimez les contournements insecure. Supprimez les entrées
insecure-registrieset toute exception HTTP pour le registre. - Automatisez le renouvellement et le rechargement. La rotation manuelle des certificats est juste le plan de panne pour le vous du futur.
- Placez un canari. Un pull périodique depuis un pool de nœuds propres détecte tôt les régressions de chaîne.
Checklist opérationnelle : avant d’accuser la PKI
- Le DNS se résout-il à l’IP attendue depuis le nœud en échec ?
- L’horloge du nœud en échec est-elle synchronisée ?
- Êtes-vous derrière un proxy d’inspection TLS corporate ?
- Utilisez-vous correctement SNI (le nom d’hôte correspond à
-servername) ? - Un load balancer a-t-il été reconfiguré récemment ?
Checklist opérationnelle : avant d’accepter « insecure registries »
- Avez-vous prouvé que le serveur manque d’intermédiaires ?
- Avez-vous prouvé que le client manque la CA racine ?
- Avez-vous vérifié si les SAN du certificat correspondent au nom d’hôte utilisé ?
- Avez-vous documenté le risque et un plan de retour arrière ?
Si vous ne pouvez pas répondre à ces questions, vous ne prenez pas une décision ; vous créez un désordre.
FAQ
1) Dois-je mettre la CA racine dans le fichier fullchain du serveur ?
Généralement non. Les serveurs envoient typiquement leaf + intermédiaires ; les clients possèdent déjà (ou devraient posséder) la racine. Envoyer la racine peut perturber certains clients et gaspiller des octets. Si votre environnement l’exige, documentez l’exception et testez largement.
2) Pourquoi mon navigateur fait confiance au registre alors que Docker ne le fait pas ?
Les navigateurs mettent en cache les intermédiaires et peuvent récupérer automatiquement les manquants. Docker sur un hôte minimal n’a souvent pas ces intermédiaires en cache et peut ne pas les récupérer. Considérez le succès navigateur comme « agréable », pas comme une preuve.
3) Où installer une CA personnalisée pour Docker ?
La meilleure pratique est d’installer votre CA racine corporate dans le store de confiance de l’OS afin que tout en profite. Docker supporte aussi des CA par registre sous /etc/docker/certs.d/<host:port>/ca.crt. Après modifications, redémarrez Docker.
4) J’ai corrigé la chaîne sur le serveur, mais les nœuds Kubernetes échouent toujours. Pourquoi ?
Kubernetes peut utiliser containerd directement, et les nœuds peuvent ne pas avoir votre CA installée dans le store système ou à l’endroit attendu par le runtime. Déboguez sur le nœud, pas sur votre poste de travail.
5) Quelle est la différence entre « unknown authority » et « valid for X, not Y » ?
« Unknown authority » est un problème de chaîne de confiance (CA/racine/intermédiaire). « Valid for X, not Y » est un problème de validation de nom d’hôte (mismatch SAN). Ce sont des corrections différentes ; ne les confondez pas.
6) Puis-je temporairement utiliser insecure-registries ?
Vous pouvez, dans le même sens que vous pouvez temporairement désactiver vos détecteurs de fumée en cuisinant. Cela peut réduire le bruit, mais enlève aussi un contrôle de sécurité. Utilisez-le seulement avec une approbation explicite et une date d’expiration planifiée.
7) Dois-je redémarrer Nginx/Ingress après avoir mis à jour les fichiers de certificat ?
Oui, vous devez au minimum recharger la configuration pour que le processus prenne en compte le nouveau certificat et la chaîne. Validez avec openssl s_client après le rechargement, pas avant.
8) Comment éviter cette panne lors de la rotation de certificats ?
Pré-distribuez les nouvelles ancres de confiance (racines/intermédiaires) avant de basculer les leafs, servez des chaînes complètes, et validez depuis des hôtes propres. Traitez la distribution des CA comme un artefact géré, pas du savoir tribal.
9) Pourquoi cela échoue uniquement sur des nœuds tout neufs ?
Les nœuds frais exposent les intermédiaires manquants et les racines manquantes parce qu’ils n’ont pas de certificats en cache, et ils ont souvent des stores de confiance minimaux et stricts. C’est pourquoi l’autoscaling est un excellent auditeur.
10) Un registre, c’est juste HTTPS, ou Docker exige quelque chose de spécial ?
Transport-wise, c’est HTTPS avec des attentes côté client. Côté application, c’est l’API Docker Registry. Si le TLS est correct, vous obtiendrez typiquement 401 Unauthorized depuis /v2/ lorsque vous n’êtes pas authentifié, ce qui est un signe de santé.
Conclusion : prochaines étapes qui ne vous hanteront pas
Corriger les erreurs TLS du registre Docker « correctement » concerne moins Docker que la discipline PKI. Servez la chaîne complète. Utilisez des SAN appropriés. Installez les bonnes ancres de confiance sur chaque machine qui récupère des images. Puis supprimez les hacks insecure ajoutés pendant la panique.
Prochaines étapes pratiques :
- Exécutez le mode opératoire de diagnostic rapide depuis le nœud en échec et capturez les sorties pour vos notes d’incident.
- Mettez à jour la terminaison TLS du registre pour servir leaf + intermédiaires (vérifiez avec
openssl s_client -showcerts). - Standardisez la distribution des CA sur les nœuds/runners (confiance OS en priorité ;
/etc/docker/certs.dseulement si nécessaire). - Ajoutez un pull canari depuis un environnement propre pour détecter les régressions de chaîne avant que vos développeurs ne les rencontrent.
- Supprimez
insecure-registrieset traitez toute réintroduction comme une exception de sécurité avec une date d’expiration.
Si vous faites ces cinq choses, le TLS du registre cesse d’être un drame récurrent et redevient ce qu’il aurait dû être : un bruit de fond que vous n’entendez jamais.