Ce guide montre comment automatiser le renouvellement de certificats TLS sans redémarrage de service. Vous allez déployer Traefik avec des certificats émis par Vault PKI, renouvelés automatiquement par Vault Agent, et rechargés dynamiquement par Traefik.
À la fin de ce guide, vous saurez :
- Configurer Vault PKI pour émettre des certificats courts (5 min pour la démo)
- Utiliser Vault Agent pour écrire
cert.pemetkey.pemsur disque - Configurer Traefik pour recharger les certificats sans restart
- Observer le renouvellement automatique en action
Prérequis : Docker Compose, un terminal Linux/macOS.
Pourquoi combiner Vault PKI, Vault Agent et Traefik ?
Section intitulée « Pourquoi combiner Vault PKI, Vault Agent et Traefik ? »Chacun des trois composants couvre une partie du problème et aucun ne suffit seul. Vault PKI est une autorité de certification interne : il émet un certificat à la demande, sans intervention humaine. Vault Agent est le processus qui réclame ce certificat, l'écrit sur disque et recommence avant l'expiration. Traefik consomme ces fichiers et sait les relire à chaud. Retirez l'une des trois briques et une opération manuelle réapparaît quelque part dans la chaîne.
Le problème des certificats statiques
Section intitulée « Le problème des certificats statiques »La gestion classique des certificats TLS pose plusieurs problèmes :
| Problème | Conséquence |
|---|---|
| Certificats longue durée (1-2 ans) | Fenêtre d'exploitation large si compromis |
| Renouvellement manuel | Oublis, expirations, interruptions de service |
| Clés privées dans les repos/configs | Risque de fuite |
| Redémarrage requis pour recharger | Downtime lors du renouvellement |
Ce que cette intégration change
Section intitulée « Ce que cette intégration change »Avec Vault PKI + Vault Agent + Traefik :
| Aspect | Avant | Après |
|---|---|---|
| Durée de vie | 1-2 ans | 24h-72h (ou moins) |
| Renouvellement | Manuel | Automatique |
| Impact sur le service | Restart requis | Aucun (rechargement dynamique) |
| Stockage des clés | Statique | Éphémère (généré à la demande) |
Architecture de la démo
Section intitulée « Architecture de la démo »Voici comment les composants interagissent :
Le flux complet :
- Vault PKI émet un certificat TLS avec un TTL court
- Vault Agent écrit
cert.pem,key.pemet met à jourtls.yml - Traefik détecte le changement de
tls.ymlet recharge les certificats - Le service (whoami) reste accessible sans interruption
- Avant expiration, Vault Agent répète le cycle
Ce que Traefik sait recharger dynamiquement
Section intitulée « Ce que Traefik sait recharger dynamiquement »Traefik distingue deux types de configuration :
| Type | Exemples | Rechargeable à chaud ? |
|---|---|---|
| Statique | Entrypoints, providers, logs | ❌ Non (restart requis) |
| Dynamique | Routers, services, middlewares, TLS | ✅ Oui |
Les certificats TLS font partie de la configuration dynamique. Traefik
peut les recharger sans redémarrage grâce au file provider avec
l'option watch: true.
Préparer l'environnement de démo
Section intitulée « Préparer l'environnement de démo »Toute la démo tient dans un dossier et un docker-compose.yml de quatre
services : Vault, le service applicatif whoami, Traefik et Vault Agent. Les
répertoires créés ci-dessous ne sont pas décoratifs : ce sont eux qui sont
montés dans les conteneurs, et un chemin manquant empêche Traefik ou Vault
Agent de démarrer.
Créer la structure du projet
Section intitulée « Créer la structure du projet »Ces trois étapes créent l'arborescence, le fichier Compose et un certificat provisoire sans lequel Traefik refuserait de servir le point d'entrée HTTPS au premier démarrage.
-
Créer les répertoires :
Fenêtre de terminal mkdir -p lab-vault-traefik/{vault-agent,traefik/{dynamic,certs},scripts}cd lab-vault-traefik -
Créer le fichier
docker-compose.yml:docker-compose.yml services:# Vault en mode dev pour la démovault:image: hashicorp/vault:1.18container_name: vaultcap_add:- IPC_LOCKenvironment:VAULT_DEV_ROOT_TOKEN_ID: rootVAULT_DEV_LISTEN_ADDRESS: 0.0.0.0:8200VAULT_ADDR: http://127.0.0.1:8200ports:- "8200:8200"networks:- vault-traefikhealthcheck:test: ["CMD", "vault", "status"]interval: 5stimeout: 3sretries: 10# Service de démowhoami:image: traefik/whoami:v1.11.0container_name: whoaminetworks:- vault-traefik# Traefik avec file providertraefik:image: traefik:v3.3container_name: traefikcommand:- "--api.insecure=true"- "--providers.file.directory=/etc/traefik/dynamic"- "--providers.file.watch=true"- "--entrypoints.web.address=:80"- "--entrypoints.websecure.address=:443"- "--log.level=INFO"ports:- "9080:80"- "9443:443"- "9088:8080" # Dashboardvolumes:- ./traefik/dynamic:/etc/traefik/dynamic:ro- ./traefik/certs:/etc/traefik/certs:ronetworks:- vault-traefikdepends_on:- whoami# Vault Agent pour le renouvellement des certificatsvault-agent:image: hashicorp/vault:1.18container_name: vault-agententrypoint: ["vault", "agent", "-config=/etc/vault-agent/config.hcl"]environment:VAULT_ADDR: http://vault:8200volumes:- ./vault-agent:/etc/vault-agent:ro- ./traefik/certs:/output/certs- ./traefik/dynamic:/output/dynamic- vault-agent-token:/run/vaultnetworks:- vault-traefikdepends_on:vault:condition: service_healthynetworks:vault-traefik:driver: bridgevolumes:vault-agent-token: -
Créer un certificat placeholder pour le démarrage initial :
Fenêtre de terminal openssl req -x509 -nodes -days 1 -newkey rsa:2048 \-keyout traefik/certs/key.pem \-out traefik/certs/cert.pem \-subj "/CN=placeholder.local.test"
Ce certificat temporaire permet à Traefik de démarrer. Vault Agent le remplacera par un vrai certificat signé par Vault PKI.
Configurer Traefik
Section intitulée « Configurer Traefik »Traefik est piloté ici entièrement par fichiers, sans étiquettes Docker,
précisément pour qu'un autre processus puisse réécrire une partie de sa
configuration. Deux fichiers cohabitent dans le répertoire surveillé :
routes.yml, écrit une fois à la main, et tls.yml, généré puis regénéré par
Vault Agent à chaque renouvellement.
Configuration dynamique des routes
Section intitulée « Configuration dynamique des routes »Créez le fichier de routes pour le service whoami :
http: routers: whoami: rule: Host(`whoami.local.test`) service: whoami entryPoints: - websecure tls: {}
# Redirection HTTP vers HTTPS whoami-redirect: rule: Host(`whoami.local.test`) service: whoami entryPoints: - web middlewares: - redirect-to-https
middlewares: redirect-to-https: redirectScheme: scheme: https permanent: true
services: whoami: loadBalancer: servers: - url: http://whoami:80Configurer Vault Agent
Section intitulée « Configurer Vault Agent »Vault Agent est un processus autonome qui s'authentifie seul auprès de Vault, maintient son token valide et rend des templates sur disque. C'est la seule brique de l'architecture qui dialogue avec Vault : ni Traefik ni l'application ne détiennent de token.
Configuration principale
Section intitulée « Configuration principale »Vault Agent utilise AppRole pour s'authentifier auprès de Vault et des templates pour écrire les certificats sur disque.
vault { address = "http://vault:8200"}
auto_auth { method "approle" { mount_path = "auth/approle" config = { role_id_file_path = "/etc/vault-agent/role_id" secret_id_file_path = "/etc/vault-agent/secret_id" remove_secret_id_file_after_reading = false } }
sink "file" { config = { path = "/run/vault/token" mode = 0644 } }}
# Template pour le certificat (cert.pem)template { source = "/etc/vault-agent/cert.tpl" destination = "/output/certs/cert.pem" perms = 0644}
# Template pour la clé privée (key.pem)template { source = "/etc/vault-agent/key.tpl" destination = "/output/certs/key.pem" perms = 0600}
# Template TLS pour Traefik - force le rechargement du file providertemplate { source = "/etc/vault-agent/traefik-tls.tpl" destination = "/output/dynamic/tls.yml" perms = 0644}
template_config { static_secret_render_interval = "5m" exit_on_retry_failure = true}Templates pour les certificats
Section intitulée « Templates pour les certificats »Ces templates demandent un nouveau certificat à Vault PKI et extraient les données nécessaires.
Template pour le certificat (cert.pem) :
{{- with secret "pki_int/issue/traefik-web" "common_name=whoami.local.test" "alt_names=whoami.local.test" "ttl=5m" -}}{{ .Data.certificate }}{{ .Data.issuing_ca }}{{- end -}}Template pour la clé privée (key.pem) :
{{- with secret "pki_int/issue/traefik-web" "common_name=whoami.local.test" "alt_names=whoami.local.test" "ttl=5m" -}}{{ .Data.private_key }}{{- end -}}Template pour la config TLS Traefik (tls.yml) :
{{- with secret "pki_int/issue/traefik-web" "common_name=whoami.local.test" "alt_names=whoami.local.test" "ttl=5m" -}}# Configuration TLS - regénéré automatiquement par Vault Agent# Serial: {{ .Data.serial_number }}# Expiration: {{ .Data.expiration }}
tls: certificates: - certFile: /etc/traefik/certs/cert.pem keyFile: /etc/traefik/certs/key.pem
stores: default: defaultCertificate: certFile: /etc/traefik/certs/cert.pem keyFile: /etc/traefik/certs/key.pem{{- end -}}Configurer Vault PKI
Section intitulée « Configurer Vault PKI »La configuration de Vault s'effectue une seule fois et enchaîne cinq
opérations : une CA racine, une CA intermédiaire signée par la
première, un rôle d'émission restreint au domaine de la démo, une policy
limitée à ce rôle, puis les identifiants AppRole que Vault Agent utilisera
pour s'authentifier. Les regrouper dans un script rend le lab rejouable après
un docker compose down.
Script d'initialisation
Section intitulée « Script d'initialisation »Ce script configure Vault PKI (Root CA + Intermediate CA), crée un rôle pour Traefik, et génère les credentials AppRole pour Vault Agent.
#!/bin/bashset -e
echo "=== Configuration Vault PKI + AppRole pour Traefik ==="
# Fonction pour exécuter vault via dockervault() { docker exec -e VAULT_ADDR=http://127.0.0.1:8200 -e VAULT_TOKEN=root vault vault "$@"}
# 1. Activer et configurer PKI Root CAecho ">>> 1. Activation PKI Root CA..."vault secrets enable -path=pki pki 2>/dev/null || echo " (pki déjà activé)"vault secrets tune -max-lease-ttl=87600h pki
vault write -field=certificate pki/root/generate/internal \ common_name="Demo Root CA" \ issuer_name="root-ca" \ ttl=87600h > /dev/null
vault write pki/config/urls \ issuing_certificates="http://vault:8200/v1/pki/ca" \ crl_distribution_points="http://vault:8200/v1/pki/crl"
echo " ✓ PKI Root CA configuré"
# 2. Activer et configurer PKI Intermediate CAecho ">>> 2. Activation PKI Intermediate CA..."vault secrets enable -path=pki_int pki 2>/dev/null || echo " (pki_int déjà activé)"vault secrets tune -max-lease-ttl=43800h pki_int
vault write -field=csr pki_int/intermediate/generate/internal \ common_name="Demo Intermediate CA" \ issuer_name="intermediate-ca" \ > /tmp/pki_int.csr
vault write -field=certificate pki/root/sign-intermediate \ csr=@/tmp/pki_int.csr \ format=pem_bundle \ ttl="43800h" \ > /tmp/int.cert.pem
vault write pki_int/intermediate/set-signed \ certificate=@/tmp/int.cert.pem
echo " ✓ PKI Intermediate CA configuré"
# 3. Créer le rôle PKI pour Traefikecho ">>> 3. Création du rôle PKI 'traefik-web'..."vault write pki_int/roles/traefik-web \ allowed_domains="local.test" \ allow_subdomains=true \ allow_bare_domains=false \ max_ttl="24h" \ ttl="5m" # TTL court pour la démo
echo " ✓ Rôle traefik-web créé (TTL: 5 min)"
# 4. Créer la policy pour Vault Agentecho ">>> 4. Création de la policy 'traefik-pki'..."vault policy write traefik-pki - <<POLICYpath "pki_int/issue/traefik-web" { capabilities = ["create", "update"]}POLICYecho " ✓ Policy traefik-pki créée"
# 5. Activer et configurer AppRoleecho ">>> 5. Configuration AppRole..."vault auth enable approle 2>/dev/null || echo " (approle déjà activé)"
vault write auth/approle/role/traefik-agent \ token_policies="traefik-pki" \ token_ttl=1h \ token_max_ttl=4h \ secret_id_ttl=0
echo " ✓ Rôle AppRole traefik-agent créé"
# 6. Récupérer les credentials AppRoleecho ">>> 6. Génération des credentials AppRole..."SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ROLE_ID=$(vault read -field=role_id auth/approle/role/traefik-agent/role-id)SECRET_ID=$(vault write -f -field=secret_id auth/approle/role/traefik-agent/secret-id)
echo "$ROLE_ID" > "$SCRIPT_DIR/../vault-agent/role_id"echo "$SECRET_ID" > "$SCRIPT_DIR/../vault-agent/secret_id"
echo " ✓ Credentials sauvegardés dans vault-agent/"
echo ""echo "=== Configuration terminée ==="echo ""echo "Prochaines étapes :"echo " 1. docker compose up -d vault-agent"echo " 2. Ajouter '127.0.0.1 whoami.local.test' à /etc/hosts"echo " 3. curl -k https://whoami.local.test:9443"Rendez le script exécutable :
chmod +x scripts/init-vault.shDéployer et tester
Section intitulée « Déployer et tester »L'ordre de démarrage compte. Vault doit être opérationnel et sa PKI configurée
avant que Vault Agent ne tente son premier rendu : avec
exit_on_retry_failure = true, l'agent s'arrête au lieu de boucler
indéfiniment sur un chemin inexistant.
Lancer l'infrastructure
Section intitulée « Lancer l'infrastructure »Cinq étapes dans cet ordre : les services de base, la configuration de Vault, l'agent, puis la résolution DNS locale sans laquelle le client ne présenterait pas le bon nom d'hôte au moment de la négociation TLS.
-
Démarrer Vault, Traefik et whoami :
Fenêtre de terminal docker compose up -d vault whoami traefikAttendez que Vault soit healthy :
Fenêtre de terminal docker compose ps -
Configurer Vault PKI :
Fenêtre de terminal ./scripts/init-vault.shVous devriez voir :
=== Configuration terminée ===Prochaines étapes :1. docker compose up -d vault-agent2. Ajouter '127.0.0.1 whoami.local.test' à /etc/hosts3. curl -k https://whoami.local.test:9443 -
Démarrer Vault Agent :
Fenêtre de terminal docker compose up -d vault-agent -
Vérifier les certificats générés :
Fenêtre de terminal ls -la traefik/certs/Vous devriez voir
cert.pemetkey.pemavec des timestamps récents. -
Ajouter l'entrée DNS locale :
Fenêtre de terminal echo "127.0.0.1 whoami.local.test" | sudo tee -a /etc/hosts
Vérifier le certificat
Section intitulée « Vérifier le certificat »Examinez le certificat émis par Vault PKI :
openssl x509 -in traefik/certs/cert.pem -noout -subject -issuer -datesRésultat attendu :
subject=CN = whoami.local.testissuer=CN = Demo Intermediate CAnotBefore=Mar 16 16:56:40 2026 GMTnotAfter=Mar 16 17:01:40 2026 GMTLe certificat est signé par "Demo Intermediate CA" et valide 5 minutes.
Tester l'accès HTTPS
Section intitulée « Tester l'accès HTTPS »L'option -k désactive la vérification de la chaîne de confiance : la CA de la
démo n'est pas connue du magasin système. La réponse de whoami confirme que
Traefik a bien terminé la connexion TLS puis relayé la requête en clair vers le
conteneur applicatif.
curl -k https://whoami.local.test:9443Résultat :
Hostname: 9b134b252f36IP: 127.0.0.1IP: 172.23.0.3RemoteAddr: 172.23.0.4:44418GET / HTTP/1.1Host: whoami.local.test:9443...Observer le renouvellement automatique
Section intitulée « Observer le renouvellement automatique »Le renouvellement est invisible depuis le client : aucune coupure, aucune erreur TLS. Pour le constater, il faut comparer le numéro de série du certificat servi d'une connexion à l'autre. Deux points d'observation se complètent, le certificat vu depuis le réseau et les journaux de Vault Agent.
Surveiller les changements de certificat
Section intitulée « Surveiller les changements de certificat »Exécutez ce script pour observer le renouvellement :
echo "=== Observation du renouvellement ==="LAST_SERIAL=""while true; do SERIAL=$(openssl s_client -connect whoami.local.test:9443 \ -servername whoami.local.test </dev/null 2>/dev/null \ | openssl x509 -noout -serial | cut -d= -f2)
DATES=$(openssl s_client -connect whoami.local.test:9443 \ -servername whoami.local.test </dev/null 2>/dev/null \ | openssl x509 -noout -enddate | cut -d= -f2)
NOW=$(date "+%H:%M:%S")
if [[ "$SERIAL" != "$LAST_SERIAL" ]] && [[ -n "$LAST_SERIAL" ]]; then echo "" echo ">>> CERTIFICAT RENOUVELÉ ! <<<" echo "" fi
LAST_SERIAL="$SERIAL" echo "[$NOW] Serial: ${SERIAL:0:20}... | Expire: $DATES"
sleep 30doneAprès environ 3-4 minutes, vous verrez le certificat changer :
[17:57:07] Serial: 2F92FE700EA7F45AFAA8... | Expire: Mar 16 17:01:39 2026 GMT[17:57:37] Serial: 2F92FE700EA7F45AFAA8... | Expire: Mar 16 17:01:39 2026 GMT[17:58:07] Serial: 2F92FE700EA7F45AFAA8... | Expire: Mar 16 17:01:39 2026 GMT
>>> CERTIFICAT RENOUVELÉ ! <<<
[17:58:37] Serial: 5193A369CE35F6173E6F... | Expire: Mar 16 17:06:11 2026 GMTVérifier les logs de Vault Agent
Section intitulée « Vérifier les logs de Vault Agent »Chaque cycle produit trois lignes rendered, une par template, séparées de
quelques millisecondes : c'est la confirmation que les trois fichiers
proviennent bien du même certificat.
docker compose logs vault-agent --tail=20 | grep renderedVous verrez les moments de renouvellement :
vault-agent | 2026-03-16T16:56:40.091Z [INFO] agent: (runner) rendered "/etc/vault-agent/cert.tpl" => "/output/certs/cert.pem"vault-agent | 2026-03-16T16:56:40.094Z [INFO] agent: (runner) rendered "/etc/vault-agent/key.tpl" => "/output/certs/key.pem"vault-agent | 2026-03-16T16:56:40.096Z [INFO] agent: (runner) rendered "/etc/vault-agent/traefik-tls.tpl" => "/output/dynamic/tls.yml"vault-agent | 2026-03-16T17:01:11.620Z [INFO] agent: (runner) rendered "/etc/vault-agent/cert.tpl" => "/output/certs/cert.pem"vault-agent | 2026-03-16T17:01:11.623Z [INFO] agent: (runner) rendered "/etc/vault-agent/key.tpl" => "/output/certs/key.pem"vault-agent | 2026-03-16T17:01:11.625Z [INFO] agent: (runner) rendered "/etc/vault-agent/traefik-tls.tpl" => "/output/dynamic/tls.yml"Ce que cette démo montre réellement
Section intitulée « Ce que cette démo montre réellement »Le point important n'est pas le nombre de conteneurs, mais la répartition des responsabilités : un seul composant détient un accès à Vault, et l'application ignore totalement son existence. C'est cette séparation qui rend le montage réutilisable pour n'importe quel autre service placé derrière le même reverse proxy.
| Composant | Responsabilité |
|---|---|
| Vault PKI | Émet des certificats courts, signés par une CA interne |
| Vault Agent | Demande et renouvelle les certificats, écrit sur disque |
| Traefik | Sert le trafic HTTPS, recharge les certificats dynamiquement |
| whoami | Service de démo, ne connaît pas l'existence de Vault |
Points clés :
- Le service (
whoami) n'a aucune dépendance directe sur Vault - Traefik recharge les certificats sans redémarrage
- Vault Agent gère tout le cycle de vie des certificats
- Si Vault est temporairement indisponible, les certificats existants restent valides jusqu'à expiration
Limites et points d'attention en production
Section intitulée « Limites et points d'attention en production »Ce lab prend des raccourcis assumés, à commencer par Vault en mode dev, dont les données vivent en mémoire et dont le token root est écrit en clair dans le fichier Compose. Trois familles de sujets demandent un traitement différent en production : la sécurité des identifiants d'amorçage, la disponibilité de Vault, et le réglage des durées de vie.
Sécurité
Section intitulée « Sécurité »Les fichiers role_id et secret_id produits par le script d'initialisation
sont les identifiants d'amorçage de l'agent : quiconque les obtient peut faire
émettre des certificats pour le domaine autorisé par le rôle.
| Aspect | Recommandation |
|---|---|
| Clé privée | Protéger key.pem avec les permissions 0600 |
| AppRole secrets | Ne pas committer role_id/secret_id dans Git |
| Vault en mode dev | Utiliser un cluster Vault en production avec TLS |
Disponibilité
Section intitulée « Disponibilité »Une panne de Vault n'interrompt pas le service immédiatement : le certificat déjà écrit sur disque continue d'être servi jusqu'à son expiration. Le TTL devient donc le délai dont vous disposez pour rétablir Vault.
| Risque | Mitigation |
|---|---|
| Vault down au démarrage | Le certificat existant reste valide jusqu'à expiration |
| Vault down au renouvellement | Vault Agent réessaie. Prévoir un TTL assez long |
| Perte du stockage Vault | Backups réguliers, cluster HA |
Configuration
Section intitulée « Configuration »Le TTL de 5 minutes n'a d'autre but que de rendre le cycle observable pendant la lecture du guide. En production, cette valeur définit à la fois la fenêtre d'exploitation d'une clé compromise et la marge de manoeuvre en cas d'indisponibilité de Vault.
| Paramètre | Recommandation production |
|---|---|
| TTL certificat | 24h-72h, assez long pour absorber une indisponibilité de Vault |
| max_ttl | 7-30 jours selon les besoins |
| allowed_domains | Restreindre aux domaines réellement utilisés |
Adapter pour la production
Section intitulée « Adapter pour la production »Trois modifications suffisent à passer du lab à un déploiement réel : allonger les durées de vie, remplacer le domaine de test par le vôtre dans le rôle et dans les templates, et abandonner AppRole si la plateforme fournit déjà une identité vérifiable, comme un ServiceAccount Kubernetes.
TTL réaliste
Section intitulée « TTL réaliste »Modifiez le rôle PKI pour un TTL de 24h :
# max_ttl : 7 jours au maximum, ttl : 24h par défautvault write pki_int/roles/traefik-web \ allowed_domains="votre-domaine.com" \ allow_subdomains=true \ max_ttl="168h" \ ttl="24h"Templates de production
Section intitulée « Templates de production »Mettez à jour les templates avec votre domaine :
{{- with secret "pki_int/issue/traefik-web" "common_name=app.votre-domaine.com" "alt_names=app.votre-domaine.com,api.votre-domaine.com" "ttl=24h" -}}{{ .Data.certificate }}{{ .Data.issuing_ca }}{{- end -}}Authentification Kubernetes
Section intitulée « Authentification Kubernetes »En environnement Kubernetes, remplacez AppRole par l'auth Kubernetes :
auto_auth { method "kubernetes" { mount_path = "auth/kubernetes" config = { role = "traefik-pki" } } # ...}À retenir
Section intitulée « À retenir »- Vault PKI génère des certificats TLS courts et signés
- Vault Agent renouvelle automatiquement avant expiration
- Traefik recharge les certificats sans redémarrage grâce au file provider
- Le template tls.yml force Traefik à détecter le changement
- Le service backend n'a aucune dépendance sur Vault
- En production, utilisez des TTL de 24-72h et un Vault sécurisé