Echecs de validation TLS intermittents avec Vault PKI et Go

Posté par paul-roland le 04/04/2026
RÉSOLU

paul-roland

Membre depuis le 19/03/2025

Hello. J'ai un souci hyper étrange sur notre mesh mTLS. On utilise HashiCorp Vault comme autorité de certification pour nos microservices en Go.

Aléatoirement, certains services refusent de se parler avec une erreur de validation de certificat. Ce qui me rend fou, c'est que ça marche 95% du temps. Les certificats sont générés par un `vault-agent` en sidecar.

L'erreur côté client Go est : `x509: certificate signed by unknown authority`.

Commentaires

fperrier

Membre depuis le 28/05/2024

Salut. Si c'est intermittent, c'est souvent un problème de chaîne de certification incomplète. Est-ce que tes services reçoivent bien l'intégralité de la chaîne (certificat + intermediates) ou juste le certificat final ?

monnier-adrien

Membre depuis le 13/03/2025

C'est une piste. Les clients Go sont très stricts sur la présence des certificats intermédiaires lors du handshake TLS si ceux-ci ne sont pas dans le trust store local du container.

Montre-nous comment ton `vault-agent` écrit les fichiers sur le disque.

paul-roland

Membre depuis le 19/03/2025

Voici ma configuration de template dans le `vault-agent-config.hcl` :

template {
  contents = "{{ with secret \"pki_int/issue/dot-com\" \"common_name=service.internal\" }}{{ .Data.certificate }}{{ end }}"
  destination = "/etc/tls/tls.crt"
}
template {
  contents = "{{ with secret \"pki_int/issue/dot-com\" \"common_name=service.internal\" }}{{ .Data.issuing_ca }}{{ end }}"
  destination = "/etc/tls/ca.crt"
}

fperrier

Membre depuis le 28/05/2024

Je vois le problème. Dans ton `tls.crt`, tu n'écris que le certificat final. Si ton client ne connaît pas l'intermediate qui a signé ce cert, il rejette la connexion.

Vault renvoie plusieurs champs : `certificate`, `issuing_ca`, et `ca_chain`.

monnier-adrien

Membre depuis le 13/03/2025

Exactement. Pour que le TLS handshake soit complet, le serveur doit envoyer son certificat ET les certificats intermédiaires. Le client, lui, n'a besoin que du Root CA dans son trust store.

Tu devrais concaténer le cert et la chaîne dans ton fichier de destination.

paul-roland

Membre depuis le 19/03/2025

Mais pourquoi ça ne plante que 5% du temps alors ? Si la chaîne manque, ça devrait planter tout le temps, non ?

fperrier

Membre depuis le 28/05/2024

Pas forcément. Si un service a déjà réussi à valider une chaîne via un autre service qui lui envoyait la chaîne complète, certains runtimes (ou l'OS) peuvent mettre l'intermediate en cache mémoire.

C'est le syndrome de l'intermediate manquant classique. On peut vérifier ça avec un `openssl s_client -connect` sur un service qui tourne.

paul-roland

Membre depuis le 19/03/2025

J'ai testé la commande sur un pod suspect :

openssl s_client -connect localhost:8443 -showcerts

Résultat : je ne vois qu'un seul certificat dans le dump. Effectivement, la chaîne est absente.

monnier-adrien

Membre depuis le 13/03/2025

Voilà. Il faut que tu modifies ton template pour inclure `ca_chain`. Attention, `ca_chain` est une liste dans la réponse JSON de Vault.

paul-roland

Membre depuis le 19/03/2025

J'ai essayé de modifier le template comme ça, mais ça me sort une erreur de syntaxe consul-template :

template {
  contents = "{{ with secret \"pki_int/issue/dot-com\" }}{{ .Data.certificate }}{{ .Data.ca_chain }}{{ end }}"
  destination = "/etc/tls/tls.crt"
}

fperrier

Membre depuis le 28/05/2024

C'est normal, `ca_chain` ne peut pas être affiché directement car c'est un objet complexe. Tu dois itérer dessus ou utiliser une fonction de jointure.

Essaie ça plutôt :

{{ .Data.certificate }}
{{ range .Data.ca_chain }}{{ . }}
{{ end }}

paul-roland

Membre depuis le 19/03/2025

J'ai appliqué le changement. Maintenant mon fichier `tls.crt` contient bien trois blocs BEGIN CERTIFICATE.

Est-ce que je dois aussi modifier le `ca.crt` utilisé par le client pour la validation ?

monnier-adrien

Membre depuis le 13/03/2025

Le `ca.crt` du client doit impérativement contenir le Root CA. Si ton `pki_int` est un intermediate, assure-toi que le client possède bien la racine tout en haut.

Si tu utilises `issuing_ca` de l'intermediate PKI, ça ne marchera que si c'est lui-même qui a signé. Pour être safe, mets le Root CA complet dans ton trust store.

paul-roland

Membre depuis le 19/03/2025

J'ai mis à jour le ConfigMap qui contient le Root CA pour tous les pods. J'ai relancé un test de charge entre mes services.

Plus aucune erreur `unknown authority` dans les logs des clients Go après 10 000 requêtes.

fperrier

Membre depuis le 28/05/2024

Excellent. C'est une erreur subtile car beaucoup de navigateurs web masquent le problème en téléchargeant l'intermediate manquant via AIA (Authority Information Access), mais les librairies TLS de backend comme celle de Go ne le font jamais.

monnier-adrien

Membre depuis le 13/03/2025

Petit conseil bonus : vérifie aussi la TTL de tes certificats Vault. Si tu génères des certs de 24h via `vault-agent`, assure-toi que ton application recharge les fichiers sur le disque sans redémarrer, sinon tu vas avoir une autre surprise demain matin.

paul-roland

Membre depuis le 19/03/2025

C'est prévu, j'utilise un watcher sur le système de fichiers pour rotate les certs dans le serveur HTTP de Go.

paul-roland

Membre depuis le 19/03/2025

Merci pour l'explication sur la chaîne, j'aurais pu chercher longtemps pourquoi ça ne plantait que par intermittence. Vous gérez !

Laisser une réponse

Vous devez être connecté pour poster un message !

Rejoindre la communauté

Recevoir les derniers articles gratuitement en créant un compte !

S'inscrire