Exercice 01 : Démarrer avec HashiCorp Vault : secrets dynamiques
🎯 Objectifs
À la fin de cet exercice, vous serez capable de :
- ✅ Lancer Vault en mode développement avec Docker
- ✅ Écrire et lire des secrets dans le moteur KV v2
- ✅ Configurer le moteur
databasepour générer des credentials PostgreSQL dynamiques - ✅ Comprendre le TTL (Time To Live) et la révocation automatique
- ✅ Utiliser les politiques Vault pour contrôler l'accès aux secrets
Durée estimée : 45 minutes
Difficulté : ⭐⭐⭐☆☆ (Avancé)
Prérequis : Docker installé, notions de bases de données PostgreSQL
📖 Contexte
La gestion de secrets traditionnelle utilise des credentials statiques : un mot de passe créé une fois et utilisé pendant des années. Si ce mot de passe est compromis, l'attaquant a un accès permanent.
Vault résout ce problème avec les secrets dynamiques : il génère un credential unique à la demande, avec une durée de vie limitée (ex: 1 heure). Quand le TTL expire, Vault révoque automatiquement le credential dans la base de données. L'attaquant qui volait un mot de passe se retrouve avec un credential déjà expiré.
📋 Énoncé
Configurez Vault pour générer des credentials PostgreSQL dynamiques avec une durée de vie de 1 heure.
🧭 Déroulement de l'exercice
Tâche 1 : Lancer Vault et PostgreSQL avec Docker Compose
mkdir vault-demo && cd vault-demo
cat > docker-compose.yml << 'EOF'
services:
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres_admin_password
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
vault:
image: hashicorp/vault:latest
cap_add:
- IPC_LOCK
environment:
VAULT_DEV_ROOT_TOKEN_ID: "root" # ⚠️ Mode dev uniquement
VAULT_DEV_LISTEN_ADDRESS: "0.0.0.0:8200"
ports:
- "8200:8200"
command: vault server -dev
EOF
docker compose up -d
sleep 3Vérification :
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='root'
vault statusDoit afficher Initialized: true, Sealed: false.
Attention : Le mode
-devest uniquement pour les tests. Il stocke tout en mémoire, sans persistance. Ne jamais utiliser en production.
Tâche 2 : Utiliser le moteur KV v2 (secrets statiques)
Le moteur KV (Key-Value) stocke des secrets statiques versionés :
# Activer le moteur KV v2 à /secret
vault secrets enable -path=secret kv-v2
# Écrire un secret
vault kv put secret/mon-app/config \
db_user=app_user \
db_password="password_tres_fort_123" \
api_key="sk_prod_abc123xyz456"
# Lire le secret
vault kv get secret/mon-app/config
# Lire un champ spécifique
vault kv get -field=db_password secret/mon-app/config
# Voir les versions (KV v2 garde l'historique)
vault kv metadata get secret/mon-app/configIndice : KV v2 garde les 10 dernières versions par défaut.
vault kv get -version=1 secret/mon-app/configlit la version 1 (ancienne version). C'est très utile pour auditer qui a modifié quoi.
Vérification : vault kv get secret/mon-app/config doit afficher les 3 champs écrits.
Tâche 3 : Configurer les secrets dynamiques pour PostgreSQL
Les secrets dynamiques génèrent des credentials uniques à la demande :
# Étape 1 : Activer le moteur Database
vault secrets enable database
# Étape 2 : Configurer la connexion à PostgreSQL
# Vault se connecte avec un compte "admin" et crée des users temporaires
vault write database/config/my-postgresql \
plugin_name=postgresql-database-plugin \
allowed_roles="app-role" \
connection_url="postgresql://{{username}}:{{password}}@postgres:5432/myapp?sslmode=disable" \
username="postgres" \
password="postgres_admin_password"
# Étape 3 : Définir un rôle = un template de credentials
# Le SQL ici crée un user PostgreSQL avec une durée de vie de 1h
vault write database/roles/app-role \
db_name=my-postgresql \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; GRANT SELECT, INSERT, UPDATE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"Tâche 4 : Générer et observer les credentials dynamiques
# Générer un credential (chaque appel génère un nouveau user unique)
vault read database/creds/app-roleSortie typique :
Key Value
lease_id database/creds/app-role/AbCdEfGh1234
lease_duration 1h
lease_renewable true
password A1B2-SecureRandomPassword
username v-root-app-role-AbCdEfGh-1234Vérifiez que PostgreSQL a bien créé l'utilisateur :
docker exec -it vault-demo-postgres-1 psql -U postgres -c "\du" | grep v-rootGénérez un second credential :
vault read database/creds/app-roleIndice : Chaque appel génère un username différent (
v-root-app-role-XXX). Si un credential est compromis, il suffit de le révoquer sans impacter les autres. En comparaison, avec un mot de passe statique, compromettre le credential force à changer LE mot de passe et redeployer toutes les applications qui l'utilisent.
Vérification : psql -U postgres -c "\du" dans le conteneur montre les deux users dynamiques créés.
Tâche 5 : Politiques d'accès Vault
Les politiques contrôlent qui peut accéder à quoi :
# Créer une politique pour l'application (lecture seule sur les creds DB)
cat > app-policy.hcl << 'EOF'
# L'application peut générer des credentials DB
path "database/creds/app-role" {
capabilities = ["read"]
}
# L'application peut lire sa configuration
path "secret/data/mon-app/*" {
capabilities = ["read"]
}
# L'application ne peut PAS écrire ou supprimer
# (pas de "create", "update", "delete" dans capabilities)
EOF
vault policy write app-policy app-policy.hcl
# Créer un token avec cette politique limitée
APP_TOKEN=$(vault token create -policy=app-policy -ttl=24h -field=token)
echo "Token app: $APP_TOKEN"
# Tester que ce token peut lire les creds mais pas les écrire
VAULT_TOKEN=$APP_TOKEN vault read database/creds/app-role # ✅ OK
VAULT_TOKEN=$APP_TOKEN vault kv put secret/mon-app/config test=value # ❌ Doit échouerVérification : La dernière commande doit échouer avec permission denied.
✅ Vérification du résultat
- Vault et PostgreSQL tournent avec
docker compose up -d vault kv get secret/mon-app/configretourne les 3 champsvault read database/creds/app-rolegénère un username unique à chaque appel- Le user PostgreSQL existe dans la base après génération
- La politique
app-policybloque les writes avecpermission denied
💡 À retenir
| Approche | Durée de vie | Révocation si compromis | Unicité |
|---|---|---|---|
| Secret statique | Illimitée | Tous les services impactés | Non |
| Secret dynamique Vault | TTL limité (1h-24h) | Révocation instantanée du lease | Oui |
Les secrets dynamiques transforment une compromission de credentials en un problème à TTL limité plutôt qu'en une porte ouverte permanente.
✨ Solution Complète
# Lancer l'environnement
docker compose up -d
export VAULT_ADDR='http://127.0.0.1:8200' VAULT_TOKEN='root'
# KV statique
vault secrets enable -path=secret kv-v2
vault kv put secret/app/config db_pass=secret123
# Dynamique
vault secrets enable database
vault write database/config/pg plugin_name=postgresql-database-plugin \
connection_url="postgresql://postgres:postgres@postgres:5432/myapp" \
username=postgres password=postgres
vault write database/roles/app db_name=pg \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';" \
default_ttl=1h
vault read database/creds/app # Génère credentials dynamiques