HashiCorp Vault sur Kubernetes : guide complet d'installation en 2026
HashiCorp Vault est l’outil de référence pour la gestion des secrets dans les architectures Cloud Native. Couplé à Kubernetes, il permet de centraliser les mots de passe, certificats, clés API et tokens, et de les distribuer aux applications de façon dynamique, auditée et sécurisée. Ce guide complet vous montre, étape par étape, comment installer Vault en haute disponibilité sur Kubernetes en 2026 — avec backend Raft intégré, TLS, intégration native via le Vault Agent Injector, backups vers S3 et bonnes pratiques opérationnelles.
Pourquoi HashiCorp Vault sur Kubernetes ?
Les Secrets Kubernetes natifs sont chiffrés au repos (depuis qu’on a activé encryption-config.yaml sur l’API server, ce qui n’est pas le cas par défaut sur tous les clusters) : c’est insuffisant en production. Vault apporte :
- une centralisation des secrets pour Kubernetes et le reste du SI (CI/CD, applications legacy, bases de données) ;
- la rotation dynamique des secrets (mots de passe DB générés à la volée, certificats PKI à courte durée de vie) ;
- un audit log complet de tous les accès ;
- un RBAC riche via les policies HCL ;
- l’intégration native Kubernetes via auth method
kubernetes, Vault Agent Injector et CSI Driver.
D’expérience, déployer Vault c’est aussi fixer une norme : les développeurs cessent de commiter des .env dans Git, et toute application reçoit ses secrets par injection au démarrage.
Prérequis
- Cluster Kubernetes 1.28+ ou OpenShift 4.14+ avec 3 nœuds workers minimum (pour la HA Raft).
- Helm 3.14+ installé localement.
- Une StorageClass dynamique (NFS, NetApp, Longhorn, EBS, Ceph…) supportant des PVC
ReadWriteOnce. - Un cert-manager ou un cluster Issuer pour générer les certificats TLS (sinon on auto-signe).
- Un bucket S3 (ou compatible — MinIO, Scaleway Object Storage) pour les snapshots Raft.
- La CLI
vault(≥ 1.18) installée sur votre poste.
Architecture cible
L’architecture recommandée en production tient en quatre points :
- 3 réplicas Vault distribués sur 3 nœuds (anti-affinity) pour tolérer la perte d’un nœud.
- Backend Raft intégré (Integrated Storage) : pas besoin de Consul, le quorum est géré par Vault lui-même.
- TLS bout-en-bout entre les pods Vault, géré par cert-manager.
- Auto-unseal via une clé KMS (AWS KMS, GCP KMS) ou un Vault de transit, pour éviter le déverrouillage manuel après chaque redémarrage.
┌─────────────────────────────────────────────────────────────┐
│ Cluster Kubernetes (3 workers) │
│ │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ vault-0 │◄──►│ vault-1 │◄──►│ vault-2 │ (Raft quorum) │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ PVC (StorageClass dynamique) │ │
│ └──────────────────────────────────────┘ │
│ │
│ Vault Agent Injector ──► injecte secrets dans pods app │
└─────────────────────────────────────────────────────────────┘
│
▼ snapshots Raft (cron quotidien)
┌──────────┐
│ Bucket S3│
└──────────┘Installation pas à pas avec Helm
1. Préparer le namespace
kubectl create namespace vault
kubectl label namespace vault pod-security.kubernetes.io/enforce=restricted
helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update2. Générer les certificats TLS avec cert-manager
# vault-tls.yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: vault-tls
namespace: vault
spec:
secretName: vault-tls
duration: 8760h # 1 an
renewBefore: 720h # renouvelle 30 j avant expiration
commonName: vault.vault.svc.cluster.local
dnsNames:
- vault
- vault.vault
- vault.vault.svc
- vault.vault.svc.cluster.local
- "*.vault-internal.vault.svc.cluster.local"
issuerRef:
name: vault-ca-issuer
kind: ClusterIssuerkubectl apply -f vault-tls.yaml
kubectl -n vault get secret vault-tls # NAME: vault-tls TYPE: kubernetes.io/tls3. Préparer le values.yaml Vault
# values-vault.yaml
global:
enabled: true
tlsDisable: false
injector:
enabled: true
replicas: 2
resources:
requests: { cpu: 50m, memory: 64Mi }
limits: { cpu: 250m, memory: 256Mi }
server:
image:
repository: hashicorp/vault
tag: "1.18.3"
resources:
requests: { cpu: 250m, memory: 512Mi }
limits: { cpu: "1", memory: "1Gi" }
ha:
enabled: true
replicas: 3
raft:
enabled: true
setNodeId: true
config: |
ui = true
listener "tcp" {
tls_disable = false
address = "[::]:8200"
cluster_address = "[::]:8201"
tls_cert_file = "/vault/userconfig/vault-tls/tls.crt"
tls_key_file = "/vault/userconfig/vault-tls/tls.key"
tls_client_ca_file = "/vault/userconfig/vault-tls/ca.crt"
}
storage "raft" {
path = "/vault/data"
}
service_registration "kubernetes" {}
seal "awskms" {
region = "eu-west-3"
kms_key_id = "alias/vault-unseal"
}
extraEnvironmentVars:
VAULT_CACERT: /vault/userconfig/vault-tls/ca.crt
volumes:
- name: vault-tls
secret:
secretName: vault-tls
volumeMounts:
- name: vault-tls
mountPath: /vault/userconfig/vault-tls
readOnly: true
dataStorage:
enabled: true
size: 20Gi
storageClass: "standard-rwo"
affinity: |
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchLabels:
app.kubernetes.io/name: {{ template "vault.name" . }}
app.kubernetes.io/instance: "{{ .Release.Name }}"
topologyKey: kubernetes.io/hostname
ui:
enabled: true
serviceType: ClusterIPQuelques points clés :
- Auto-unseal AWS KMS : si vous n’avez pas KMS, retirez la stanza
sealet déverrouillez manuellement (cf. plus bas). - Anti-affinity : indispensable pour ne pas se retrouver avec les 3 réplicas sur le même nœud.
- Resources : ces valeurs sont un plancher. À ajuster sous charge.
4. Installer Vault
helm install vault hashicorp/vault \
-n vault \
-f values-vault.yaml
# Vérifier
kubectl -n vault get pods -w
# vault-0 0/1 Running (sealed)
# vault-1 0/1 Running (sealed)
# vault-2 0/1 Running (sealed)
# vault-agent-injector-xxx 1/1 RunningLes pods Vault sont en 0/1 Running tant que le cluster n’a pas été initialisé : c’est normal.
Initialisation et déverrouillage du cluster
# Initialiser sur le pod vault-0
kubectl -n vault exec -it vault-0 -- vault operator init \
-key-shares=5 -key-threshold=3 -format=json > vault-init.json
# vault-init.json contient les 5 unseal keys et le root token
# →→→ STOCKEZ-LE DANS UN COFFRE OFFLINE OU UN AUTRE VAULT ←←←
# Si pas d'auto-unseal KMS, déverrouiller manuellement (3 clés sur 5)
for K in $(jq -r '.unseal_keys_b64[0,1,2]' vault-init.json); do
kubectl -n vault exec vault-0 -- vault operator unseal "$K"
done
# Joindre vault-1 et vault-2 au cluster Raft
for POD in vault-1 vault-2; do
kubectl -n vault exec $POD -- \
vault operator raft join \
-address=https://$POD.vault-internal:8200 \
-leader-ca-cert="$(cat ca.crt)" \
https://vault-0.vault-internal:8200
for K in $(jq -r '.unseal_keys_b64[0,1,2]' vault-init.json); do
kubectl -n vault exec $POD -- vault operator unseal "$K"
done
done
# Vérifier le quorum
kubectl -n vault exec vault-0 -- vault operator raft list-peers
# 3 nœuds attendus, l'un d'entre eux en leaderVous pouvez maintenant ouvrir l’UI : kubectl -n vault port-forward svc/vault 8200:8200 puis https://localhost:8200, et vous connecter avec le root token de vault-init.json.
Configurer l’authentification Kubernetes
L’objectif : permettre à un pod (via son ServiceAccount) de s’authentifier auprès de Vault sans mot de passe partagé.
# Activer la méthode kubernetes
vault auth enable kubernetes
# Configurer Vault pour valider les tokens auprès de l'API server
TOKEN_REVIEW_JWT=$(kubectl -n vault create token vault \
--duration=8760h --audience=https://kubernetes.default.svc)
vault write auth/kubernetes/config \
token_reviewer_jwt="$TOKEN_REVIEW_JWT" \
kubernetes_host="https://kubernetes.default.svc" \
kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \
disable_iss_validation=true
# Créer un secret KV pour l'application "demo"
vault secrets enable -path=secret kv-v2
vault kv put secret/demo/config api_token="changeme123"
# Policy donnant accès en lecture à secret/demo/*
vault policy write demo-read - <<EOF
path "secret/data/demo/*" {
capabilities = ["read"]
}
EOF
# Mapper ServiceAccount → policy
vault write auth/kubernetes/role/demo \
bound_service_account_names=demo \
bound_service_account_namespaces=demo \
policies=demo-read \
ttl=24hInjecter des secrets dans un pod (Vault Agent Injector)
# demo-app.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: demo
namespace: demo
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
namespace: demo
spec:
replicas: 1
selector:
matchLabels: { app: demo }
template:
metadata:
labels: { app: demo }
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "demo"
vault.hashicorp.com/agent-inject-secret-config.env: "secret/data/demo/config"
vault.hashicorp.com/agent-inject-template-config.env: |
{{- with secret "secret/data/demo/config" -}}
API_TOKEN={{ .Data.data.api_token }}
{{- end -}}
spec:
serviceAccountName: demo
containers:
- name: app
image: ghcr.io/example/demo:1.0
command: ["/bin/sh", "-c", "source /vault/secrets/config.env && exec /app"]L’injecteur démarre un sidecar vault-agent, s’authentifie auprès de Vault via le token du ServiceAccount, récupère le secret et le matérialise dans /vault/secrets/config.env. Pas de variable secrète dans le manifeste, pas de kubectl create secret à faire.
Alternative : le Secrets Store CSI Driver
Le Secrets Store CSI Driver est plus moderne : il monte les secrets sous forme de volumes (et optionnellement les synchronise comme Secret Kubernetes). Plus de sidecar, mais une dépendance au CSI Driver et au Vault Provider.
helm repo add secrets-store-csi-driver \
https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
helm install csi -n kube-system secrets-store-csi-driver/secrets-store-csi-driver \
--set syncSecret.enabled=true
helm install vault hashicorp/vault \
-n vault --reuse-values \
--set csi.enabled=trueLe CSI Driver est particulièrement adapté quand vos applications attendent un fichier monté (TLS cert, kubeconfig, Java keystore) plutôt qu’une variable d’environnement.
Sauvegarde et restauration
Vault Raft offre un mécanisme de snapshot natif. La bonne pratique : un cron quotidien qui pousse le snapshot vers S3.
apiVersion: batch/v1
kind: CronJob
metadata:
name: vault-snapshot
namespace: vault
spec:
schedule: "0 2 * * *"
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
serviceAccountName: vault-snapshot
containers:
- name: snapshot
image: hashicorp/vault:1.18.3
env:
- { name: VAULT_ADDR, value: https://vault.vault.svc:8200 }
- { name: VAULT_CACERT, value: /tls/ca.crt }
- name: VAULT_TOKEN
valueFrom: { secretKeyRef: { name: vault-snapshot-token, key: token } }
command:
- /bin/sh
- -c
- |
vault operator raft snapshot save /tmp/snap.db
aws s3 cp /tmp/snap.db s3://my-bucket/vault/$(date +%Y%m%d).db
volumeMounts:
- { name: tls, mountPath: /tls, readOnly: true }
volumes:
- { name: tls, secret: { secretName: vault-tls } }Pour restaurer, c’est l’inverse :
kubectl -n vault cp snap.db vault-0:/tmp/snap.db
kubectl -n vault exec vault-0 -- vault operator raft snapshot restore /tmp/snap.dbTestez vos restaurations au moins une fois par trimestre sur un cluster de pré-production. Un backup non testé n’est pas un backup.
Et OpenBao, l’alternative open source ?
Depuis la bascule de Vault sous licence Business Source License (BSL) en 2023, la Linux Foundation a forké le projet sous le nom OpenBao. La 1.0 est sortie en 2024 et le projet est compatible quasi 1-pour-1 avec Vault.
- Le chart Helm est
openbao/openbao. - Les commandes CLI sont identiques :
baoest un alias devault. - Le format des policies, les auth methods et les secrets engines sont les mêmes.
OpenBao est un excellent choix si vous voulez rester en 100% open source ou éviter la BSL. C’est la voie que je recommande aujourd’hui pour la majorité des nouvelles installations chez mes clients qui n’ont pas de souscription HashiCorp existante.
FAQ
Faut-il vraiment 3 réplicas Vault ?
Pour le quorum Raft, oui. Avec 1 réplica, vous perdez Vault dès que le pod redémarre. Avec 3, vous tolérez la perte d’un pod ou d’un nœud sans interruption.
Vault Enterprise ou Vault Community ?
La version Community couvre la plupart des besoins (auth methods, KV, PKI, audit). Vault Enterprise apporte la réplication multi-datacenter (DR + Performance), les namespaces et le HSM. Pour un site unique, la version Community suffit.
Comment auditer les accès aux secrets ?
Activez un audit device Vault, par exemple vault audit enable file file_path=/vault/audit/log ou vault audit enable socket address=splunk:9000 socket_type=tcp. Tous les appels API y sont tracés (et les valeurs sont hashées).
Combien de temps pour mettre Vault en production ?
D’expérience, comptez 2 à 3 jours pour un déploiement HA propre (TLS, auto-unseal, monitoring, backup), puis 1 à 2 semaines pour migrer les premières applications. La gouvernance des policies et la formation des équipes prennent souvent plus de temps que l’install elle-même.
Besoin d’aide pour déployer Vault chez vous ?
Architecture HA, intégration Kubernetes, PKI interne, formation des équipes — c’est l’un de mes domaines de prédilection. Décrivez-moi votre contexte et je vous propose un plan d’attaque sous 24 heures.
Pour aller plus loin : Kubernetes vs OpenShift, quelle différence en 2026 ?