Aller au contenu
Sécurité medium

Vault KV : stocker et versionner vos secrets statiques

18 min de lecture

logo vault

Le secrets engine KV (Key-Value) est le plus utilisé dans Vault. Il stocke des paires clé/valeur comme des mots de passe, clés API ou tokens. La version KV v2 ajoute le versioning : chaque modification crée une nouvelle version, permettant de récupérer ou restaurer des valeurs précédentes.

Ce guide couvre toutes les opérations sur les secrets KV v2, du stockage basique au contrôle de concurrence avec Check-and-Set.

  • Vault installé et démarré (mode dev ou production)
  • Variables d’environnement configurées (VAULT_ADDR, VAULT_TOKEN)

Vault propose deux versions du secrets engine KV :

AspectKV v1KV v2
VersioningNonOui (conserve les versions)
Soft deleteNonOui (récupérable)
Check-and-SetNonOui (évite les écrasements)
MétadonnéesBasiquesComplètes (timestamps, custom)
Cas d’usageLegacy, très haute performanceStandard, recommandé
Fenêtre de terminal
# Activer KV v2 sur le path "secret/"
vault secrets enable -path=secret kv-v2
# Vérifier l'activation
vault secrets list | grep secret

La commande vault kv put crée ou met à jour un secret.

Fenêtre de terminal
# Forme classique : path complet
vault kv put secret/chemin/du/secret clé1="valeur1" clé2="valeur2"
# Forme avec -mount (plus explicite)
vault kv put -mount=secret chemin/du/secret clé1="valeur1" clé2="valeur2"

Imaginons une application web qui a besoin de se connecter à une base de données PostgreSQL :

Fenêtre de terminal
vault kv put -mount=secret apps/webapp/database \
host="db.example.com" \
port="5432" \
username="webapp_user" \
password="SuperSecret123!"

Sortie :

===== Secret Path =====
secret/data/apps/webapp/database
======= Metadata =======
Key Value
--- -----
created_time 2026-03-16T13:32:40.176016777Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1

Comprendre les chemins API (path réel vs logique)

Section intitulée « Comprendre les chemins API (path réel vs logique) »

Vous écrivez sur secret/apps/webapp/database, mais Vault stocke sur secret/data/apps/webapp/database. Pourquoi ?

KV v2 utilise des sous-chemins API pour séparer les opérations :

OpérationChemin APIExemple
Données (CRUD)secret/data/...secret/data/apps/webapp/database
Métadonnéessecret/metadata/...secret/metadata/apps/webapp/database
Suppression (versions)secret/delete/...secret/delete/apps/webapp/database
Restaurationsecret/undelete/...secret/undelete/apps/webapp/database
Destructionsecret/destroy/...secret/destroy/apps/webapp/database

La CLI masque cette complexité, mais vous la verrez dans :

  • les policies Vault (capabilities sur secret/data/*)
  • l’API HTTP directe
  • les intégrations tierces (Terraform, Kubernetes operator…)

Vault accepte plusieurs formats d’entrée. Pour des secrets complexes, vous pouvez lire depuis un fichier.

Option 1 : Paires clé=valeur depuis stdin

Fenêtre de terminal
# stdin avec le préfixe @- (tiret = stdin)
echo '{"api_key":"sk-abc123","api_secret":"secret-xyz789"}' | \
vault kv put -mount=secret apps/webapp/api-config -

Le contenu JSON est interprété comme plusieurs paires clé=valeur. Vault stockera deux champs : api_key et api_secret.

Option 2 : Stocker un blob entier

Si vous voulez stocker un fichier entier (certificat, clé privée) comme une seule valeur :

Fenêtre de terminal
vault kv put -mount=secret apps/webapp/tls-cert \
cert="$(cat server.crt)" \
key="$(cat server.key)"

Option 3 : Fichier JSON avec @

Fenêtre de terminal
# Le fichier doit contenir un objet JSON avec les paires clé-valeur
cat > creds.json << 'EOF'
{
"username": "admin",
"password": "secret123"
}
EOF
vault kv put -mount=secret apps/webapp/creds @creds.json
# Nettoyer
rm creds.json

Cette commande crée un secret avec deux champs : username et password.

Fenêtre de terminal
vault kv get -mount=secret apps/webapp/database

Sortie :

===== Secret Path =====
secret/data/apps/webapp/database
======= Metadata =======
Key Value
--- -----
created_time 2026-03-16T13:32:40.176016777Z
custom_metadata <nil>
deletion_time n/a
destroyed false
version 1
====== Data ======
Key Value
--- -----
host db.example.com
password SuperSecret123!
port 5432
username webapp_user

Pour l’intégration dans des scripts, extrayez une seule valeur :

Fenêtre de terminal
# Extraire le mot de passe
vault kv get -mount=secret -field=password apps/webapp/database
# SuperSecret123!

Pour le parsing automatisé, utilisez le format JSON :

Fenêtre de terminal
vault kv get -mount=secret -format=json apps/webapp/database | \
jq -r '.data.data.password'
# SuperSecret123!

Quand plusieurs processus écrivent sur le même secret, il y a un risque d’écrasement silencieux. Check-and-Set (CAS) résout ce problème.

Sans CAS, si deux processus modifient le même secret en parallèle :

  1. Processus A lit la version 5
  2. Processus B lit la version 5
  3. Processus A écrit → version 6
  4. Processus B écrit → version 7, écrasant les changements de A

L’option -cas=<version> impose que l’écriture ne réussisse que si la version courante correspond :

Fenêtre de terminal
# Écrire seulement si la version actuelle est 2
vault kv put -mount=secret -cas=2 apps/webapp/database \
password="NewSecurePassword!"

Si un autre processus a modifié le secret entre-temps :

Error writing data to secret/data/apps/webapp/database: Error making API request.
Code: 400. Errors:
* check-and-set parameter did not match the current version

-cas=0 crée le secret uniquement s’il n’existe pas :

Fenêtre de terminal
# Échoue si le secret existe déjà
vault kv put -mount=secret -cas=0 apps/webapp/new-secret \
api_key="sk-newkey123"

Vous pouvez imposer l’usage de CAS pour tous les écrivains :

Fenêtre de terminal
# Toute écriture sans -cas échouera
vault kv metadata put -mount=secret -cas-required=true apps/webapp/database

Tentative d’écriture sans CAS :

Error writing data to secret/data/apps/webapp/database: Error making API request.
Code: 400. Errors:
* check-and-set parameter required for this call

Chaque modification d’un secret crée une nouvelle version. C’est l’un des avantages majeurs de KV v2.

Quand vous modifiez un secret, la version incrémente automatiquement :

Fenêtre de terminal
# Mise à jour du mot de passe (crée version 2)
vault kv put -mount=secret apps/webapp/database \
host="db.example.com" \
port="5432" \
username="webapp_user" \
password="NewPassword456!"

Sortie :

...
version 2

Vous pouvez récupérer n’importe quelle version antérieure :

Fenêtre de terminal
# Lire la version 1 (ancien mot de passe)
vault kv get -mount=secret -version=1 apps/webapp/database
Fenêtre de terminal
vault kv metadata get -mount=secret apps/webapp/database

Sortie (extrait) :

======= Metadata =======
Key Value
--- -----
cas_required false
created_time 2026-03-16T13:32:40.176016777Z
current_version 2
delete_version_after 0s
max_versions 0
oldest_version 0
updated_time 2026-03-16T13:45:12.234567890Z
====== Version 1 ======
Key Value
--- -----
created_time 2026-03-16T13:32:40.176016777Z
deletion_time n/a
destroyed false
====== Version 2 ======
Key Value
--- -----
created_time 2026-03-16T13:45:12.234567890Z
deletion_time n/a
destroyed false

Si une mise à jour pose problème, vous pouvez “rollback” :

Fenêtre de terminal
# Restaurer la version 1 comme nouvelle version (crée version 3)
vault kv rollback -mount=secret -version=1 apps/webapp/database

KV v2 distingue plusieurs niveaux de suppression. Le modèle mental :

ActionEffetRéversible ?
deleteMasque la version (soft delete)✅ Oui (undelete)
undeleteRend la version visible à nouveau
destroySupprime les données de la version❌ Non
metadata deleteSupprime tout : versions + métadonnées❌ Non

Marque la version comme supprimée mais conserve les données :

Fenêtre de terminal
vault kv delete -mount=secret apps/webapp/database

Les données ne sont plus accessibles par un get normal :

Fenêtre de terminal
vault kv get -mount=secret apps/webapp/database
# Affiche uniquement les métadonnées, pas les données

Annule un soft delete :

Fenêtre de terminal
# Restaurer la version 2
vault kv undelete -mount=secret -versions=2 apps/webapp/database

Les données sont à nouveau accessibles.

Supprime définitivement les données d’une ou plusieurs versions :

Fenêtre de terminal
# Supprimer définitivement les versions 1 et 2
vault kv destroy -mount=secret -versions=1,2 apps/webapp/database

Pour supprimer complètement un secret (toutes versions + métadonnées) :

Fenêtre de terminal
vault kv metadata delete -mount=secret apps/webapp/database

Cette commande supprime tout : le secret n’existe plus dans Vault.

Une bonne organisation facilite la gestion des policies et la maintenance. La structure de vos chemins détermine directement vos policies de sécurité.

secret/
├── apps/ # Secrets applicatifs
│ ├── webapp/
│ │ ├── database # Credentials DB
│ │ ├── api-keys # Clés API tierces
│ │ └── config # Config sensible
│ └── mobile-app/
│ └── firebase
├── infra/ # Secrets infrastructure
│ ├── aws/
│ │ └── credentials
│ └── docker-registry/
│ └── credentials
└── teams/ # Secrets par équipe
├── dev/
└── ops/

Cette hiérarchie permet d’écrire des policies précises sans wildcards trop larges :

# Policy pour l'application webapp (lecture seule)
path "secret/data/apps/webapp/*" {
capabilities = ["read"]
}
# Policy pour l'équipe ops (lecture/écriture sur infra)
path "secret/data/infra/*" {
capabilities = ["create", "update", "read", "delete"]
}
path "secret/metadata/infra/*" {
capabilities = ["list", "read"]
}
CritèreExemple de cheminAvantage
Par applicationsecret/apps/webapp/...Isolation par service
Par environnementsecret/prod/..., secret/dev/...Séparation prod/dev
Par équipesecret/teams/backend/...Ownership clair
Par typesecret/databases/..., secret/api-keys/...Gestion transverse

Combinez ces critères selon votre contexte : secret/prod/apps/webapp/database ou secret/apps/webapp/prod/database.

Fenêtre de terminal
# Lister les secrets sous un chemin
vault kv list -mount=secret apps/

Sortie :

Keys
----
webapp/
mobile-app/

Ajoutez des métadonnées pour documenter vos secrets :

Fenêtre de terminal
vault kv metadata put -mount=secret \
-custom-metadata="owner=team-backend" \
-custom-metadata="environment=production" \
-custom-metadata="rotation=90days" \
apps/webapp/database

Par défaut, Vault conserve toutes les versions. Cela peut poser problème :

  • Croissance du stockage
  • Conservation d’anciennes valeurs sensibles plus longtemps que nécessaire

Limitez avec -max-versions :

Fenêtre de terminal
# Conserver uniquement les 10 dernières versions
vault kv metadata put -mount=secret -max-versions=10 apps/webapp/database

Configurez un délai avant suppression définitive des versions soft-deleted :

Fenêtre de terminal
# Supprimer définitivement 24h après un delete
vault kv metadata put -mount=secret -delete-version-after=24h apps/webapp/database

Ce guide montre la gestion via CLI. En production, vos applications consomment les secrets via d’autres méthodes :

MéthodeUsage typique
API HTTPIntégration directe dans le code
Agent + templatesInjection dans fichiers de config
CSI driverMontage comme volume Kubernetes
Vault Secrets OperatorSynchronisation vers Secrets K8s

KV v2 améliore énormément la gestion des secrets statiques, mais il a des limites structurelles :

CapacitéKV v2Alternative Vault
Générer des secrets dynamiquesDatabase, AWS, Azure
Rotation automatique côté cibleDatabase secrets engine
Chiffrement de données applicativesTransit
Certificats X.509PKI
Clés SSH signéesSSH secrets engine

Règle simple : si vous pouvez éviter un secret statique, faites-le.

  • Base de données → Database secrets engine (credentials éphémères)
  • Cloud provider → AWS/Azure/GCP secrets engine
  • Certificats → PKI secrets engine
  • SSH → SSH secrets engine avec certificats

KV v2 reste indispensable pour :

  • Clés API tierces (imposées par le fournisseur)
  • Secrets legacy (systèmes non intégrables)
  • Tokens partagés (Slack webhooks, API tokens…)
  • Bootstrap secrets (premiers credentials pour accéder au reste)
SymptômeCause probableSolution
no value found at secret/data/...Secret inexistant ou suppriméVérifier le path, utiliser undelete
permission deniedPolicy insuffisanteVérifier les capabilities read, list sur secret/data/...
check-and-set parameter did not matchÉcriture concurrenteRelire la version actuelle, utiliser -cas=<version>
check-and-set parameter requiredcas-required=true sur la cléAjouter -cas=<version> à la commande
Version non trouvéeVersion détruiteVérifier avec metadata get
Versions anciennes inaccessiblesmax-versions atteintAugmenter la limite ou accepter la perte
  1. KV v2 est le standard pour les secrets statiques — préférez les secrets dynamiques quand c’est possible
  2. Check-and-Set (-cas) évite les écrasements concurrents — activez -cas-required sur les secrets critiques
  3. La CLI masque les chemins API — vos policies doivent cibler secret/data/* et secret/metadata/*
  4. delete = soft delete récupérable, destroy = définitif, metadata delete = suppression totale
  5. Organisez vos secrets pour faciliter les policies : par application/environnement/équipe
  6. Les métadonnées personnalisées documentent mais ne déclenchent rien
  7. Limitez les versions avec -max-versions pour éviter la croissance infinie et la rétention excessive

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracing. Aujourd'hui, ce site ne couvre même pas mes frais d'hébergement, d'électricité, de matériel, de logiciels, mais surtout de cafés.

Un soutien régulier, même symbolique, m'aide à garder ces ressources gratuites et à continuer de produire des guides de qualité. Merci pour votre appui.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn