Vault Database secrets engine génère des credentials de base de données à la demande, avec une durée de vie limitée. Chaque service obtient son propre user/password, révocable individuellement.
Ce guide couvre PostgreSQL, MySQL et MongoDB, mais le principe s’applique à tous les plugins database supportés par Vault.
Pourquoi des credentials dynamiques pour les bases de données
Section intitulée « Pourquoi des credentials dynamiques pour les bases de données »| Modèle classique | Avec Vault Database |
|---|---|
Un compte partagé app_user | Un compte par workload v-token-app-xyz |
| Password dans le code/env | Password généré à la demande |
| Rotation manuelle annuelle | Dynamic role : émission éphémère + révocation |
| Compromission = tout exposé | Compromission = un seul workload |
| Audit : “app_user a fait X” | Audit : “v-token-svc-abc a fait X” |
Prérequis
Section intitulée « Prérequis »- Vault installé et démarré
- Accès admin pour configurer le moteur
- Base de données PostgreSQL, MySQL ou MongoDB accessible depuis Vault
- Compte de gestion dédié à Vault pour créer/supprimer des utilisateurs
Architecture du moteur Database
Section intitulée « Architecture du moteur Database »Composants :
- Connection : configuration de l’accès à la DB (URL, admin credentials)
- Role : template SQL + TTL pour les credentials générés
- Lease : référence au credential émis, pour renouvellement/révocation
Étape 1 : activer le moteur et configurer la connexion
Section intitulée « Étape 1 : activer le moteur et configurer la connexion »Activer le moteur
Section intitulée « Activer le moteur »vault secrets enable databaseConfigurer la connexion PostgreSQL
Section intitulée « Configurer la connexion PostgreSQL »vault write database/config/postgres \ plugin_name="postgresql-database-plugin" \ allowed_roles="readonly,readwrite" \ connection_url="postgresql://{{username}}:{{password}}@postgres.example.com:5432/myapp?sslmode=require" \ username="vault_admin" \ password="VaultAdminPassword123"vault write database/config/mysql \ plugin_name="mysql-database-plugin" \ allowed_roles="readonly,readwrite" \ connection_url="{{username}}:{{password}}@tcp(mysql.example.com:3306)/myapp" \ username="vault_admin" \ password="VaultAdminPassword123"vault write database/config/mongodb \ plugin_name="mongodb-database-plugin" \ allowed_roles="readonly,readwrite" \ connection_url="mongodb://{{username}}:{{password}}@mongo.example.com:27017/admin?tls=true" \ username="vault_admin" \ password="VaultAdminPassword123"Rotation du mot de passe admin
Section intitulée « Rotation du mot de passe admin »Après configuration, faites tourner le mot de passe admin pour que seul Vault le connaisse :
vault write -force database/rotate-root/postgresÉtape 2 : créer un rôle dynamique
Section intitulée « Étape 2 : créer un rôle dynamique »Un rôle définit :
- Le template SQL pour créer l’utilisateur
- Les permissions accordées
- Le TTL (durée de vie)
vault write database/roles/readonly \ db_name="postgres" \ creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \ GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \ revocation_statements="REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM \"{{name}}\"; \ DROP ROLE IF EXISTS \"{{name}}\";" \ default_ttl="1h" \ max_ttl="24h"vault write database/roles/readwrite \ db_name="postgres" \ creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \ GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \ revocation_statements="REVOKE ALL PRIVILEGES ON ALL TABLES IN SCHEMA public FROM \"{{name}}\"; \ DROP ROLE IF EXISTS \"{{name}}\";" \ default_ttl="1h" \ max_ttl="8h"vault write database/roles/readonly \ db_name="mysql" \ creation_statements="CREATE USER '{{name}}'@'%' IDENTIFIED BY '{{password}}'; \ GRANT SELECT ON myapp.* TO '{{name}}'@'%';" \ revocation_statements="DROP USER IF EXISTS '{{name}}'@'%';" \ default_ttl="1h" \ max_ttl="24h"Variables disponibles :
| Variable | Description |
|---|---|
{{name}} | Username généré par Vault |
{{password}} | Password généré par Vault |
{{expiration}} | Date d’expiration (PostgreSQL VALID UNTIL) |
Étape 3 : émettre des credentials
Section intitulée « Étape 3 : émettre des credentials »vault read database/creds/readonlySortie :
Key Value--- -----lease_id database/creds/readonly/abcd1234-5678-90eflease_duration 1hlease_renewable truepassword A1B2-c3D4-e5F6-g7H8username v-token-readonly-xyz123abcCe qui se passe :
- Vault se connecte à PostgreSQL avec le compte admin
- Vault exécute le
creation_statementsavec un username/password générés - Vault retourne les credentials et crée un lease
- L’application utilise ces credentials
- À expiration (ou révocation), Vault exécute
revocation_statements
Vérifier côté PostgreSQL
Section intitulée « Vérifier côté PostgreSQL »-- Lister les utilisateurs VaultSELECT usename, valuntil FROM pg_user WHERE usename LIKE 'v-%';
-- Résultat usename | valuntil--------------------------+------------------------ v-token-readonly-xyz123 | 2026-03-16 15:00:00+00Static role : rotation d’un compte existant
Section intitulée « Static role : rotation d’un compte existant »Pour les applications legacy qui ne supportent pas les credentials dynamiques, Vault peut gérer un compte existant et le faire tourner automatiquement.
Créer un static role
Section intitulée « Créer un static role »vault write database/static-roles/legacy-app \ db_name="postgres" \ username="legacy_app_user" \ rotation_statements="ALTER ROLE \"{{name}}\" WITH PASSWORD '{{password}}';" \ rotation_period="24h"Lire les credentials
Section intitulée « Lire les credentials »vault read database/static-creds/legacy-appSortie :
Key Value--- -----last_vault_rotation 2026-03-16T10:00:00Zpassword NewRotatedPass123rotation_period 24httl 23h45musername legacy_app_userDifférence avec dynamic role :
- Le username est toujours le même (
legacy_app_user) - Le password change automatiquement tous les
rotation_period - Pas de lease à renouveler, mais le password change
Choisir dynamic role ou static role
Section intitulée « Choisir dynamic role ou static role »| Cas d’usage | Choix | Raison |
|---|---|---|
| Nouvelle application | Dynamic role | Traçabilité par workload |
| Appli legacy avec user fixe imposé | Static role | Pas d’alternative |
| Besoin d’audit précis par service | Dynamic role | Username unique = audit clair |
| Outil sans support de relecture credentials | Static role | Password stable entre rotations |
| Connexions éphémères (jobs, CI) | Dynamic role | TTL très court possible |
| Connexion longue durée (pool) | Dynamic role + renouvellement | Ou static role si legacy |
Révocation et renouvellement
Section intitulée « Révocation et renouvellement »Renouveler un lease
Section intitulée « Renouveler un lease »Avant expiration, l’application peut prolonger :
vault lease renew database/creds/readonly/abcd1234-5678-90efLe renouvellement est limité par le max_ttl du rôle.
Révoquer un lease
Section intitulée « Révoquer un lease »# Révoquer un credential spécifiquevault lease revoke database/creds/readonly/abcd1234-5678-90ef
# Révoquer tous les credentials d'un rôlevault lease revoke -prefix database/creds/readonlyAction réelle : Vault exécute les revocation_statements et supprime
l’utilisateur de la base de données.
Forcer une rotation (static role)
Section intitulée « Forcer une rotation (static role) »vault write -force database/rotate-role/legacy-appPolicy pour l’accès database
Section intitulée « Policy pour l’accès database »# Obtenir des credentials readonlypath "database/creds/readonly" { capabilities = ["read"]}
# Renouveler ses propres leasespath "sys/leases/renew" { capabilities = ["update"]}vault policy write app-readonly policy-app-readonly.hclIntégration applicative
Section intitulée « Intégration applicative »Exemple Python avec psycopg2
Section intitulée « Exemple Python avec psycopg2 »import hvacimport psycopg2from contextlib import contextmanager
def get_db_credentials(): """Obtenir des credentials dynamiques depuis Vault.""" client = hvac.Client(url='https://vault.example.com') # Auth via AppRole, Kubernetes, etc. client.auth.approle.login( role_id='app-role-id', secret_id='app-secret-id' )
# Lire les credentials dynamiques creds = client.secrets.database.generate_credentials( name='readonly', mount_point='database' )
return { 'user': creds['data']['username'], 'password': creds['data']['password'], 'lease_id': creds['lease_id'], 'lease_duration': creds['lease_duration'] }
@contextmanagerdef get_db_connection(): """Context manager avec credentials dynamiques.""" creds = get_db_credentials()
conn = psycopg2.connect( host='postgres.example.com', database='myapp', user=creds['user'], password=creds['password'] )
try: yield conn finally: conn.close() # Optionnel : révoquer immédiatement si TTL court # client.sys.revoke_lease(creds['lease_id'])
# Utilisationwith get_db_connection() as conn: cursor = conn.cursor() cursor.execute("SELECT * FROM users LIMIT 10") rows = cursor.fetchall()Vault Agent avec template
Section intitulée « Vault Agent avec template »Pour les applications qui ne peuvent pas appeler Vault directement :
template { contents = <<EOF[database]host = postgres.example.comport = 5432database = myapp{{ with secret "database/creds/readonly" }}username = {{ .Data.username }}password = {{ .Data.password }}{{ end }}EOF destination = "/etc/myapp/db.conf" perms = "0600" command = "systemctl reload myapp"}Dépannage
Section intitulée « Dépannage »| Symptôme | Cause probable | Solution |
|---|---|---|
permission denied | Policy manquante | Vérifier database/creds/<role> |
connection refused | Vault ne peut pas joindre la DB | Vérifier réseau/firewall |
authentication failed | Credentials admin invalides | Vérifier la config connection |
role not found | Rôle inexistant ou mal nommé | vault list database/roles |
TTL exceeds max | TTL demandé > max_ttl | Réduire le TTL ou augmenter max_ttl |
| User non supprimé après révocation | revocation_statements incorrect | Vérifier la syntaxe SQL |
Debug de la connexion
Section intitulée « Debug de la connexion »# Vérifier la configurationvault read database/config/postgres
# Tester une émissionvault read database/creds/readonly
# Vérifier les leases actifsvault list sys/leases/lookup/database/creds/readonlyCe que le moteur Database ne fait pas
Section intitulée « Ce que le moteur Database ne fait pas »| Responsabilité | Vault | Vous |
|---|---|---|
| Créer des users dynamiques | ✅ | — |
| Révoquer à expiration | ✅ | — |
| Stocker les credentials | ❌ | ✅ (le temps d’un lease) |
| Gérer le pool de connexions | ❌ | ✅ |
| Migrer les apps existantes | ❌ | ✅ |
| Monitorer les expirations | ❌* | ✅ |
| Backup de la DB | ❌ | ✅ |
*Vault expose des métriques, mais le monitoring est à votre charge.
À retenir
Section intitulée « À retenir »- Dynamic role : nouveau credential par requête, traçabilité parfaite
- Static role : rotation périodique d’un compte existant (legacy uniquement)
- Connection : compte de gestion dédié à Vault (pas le superuser DB)
rotate-root: après config, seul Vault connaît le mot de passe de gestion- Lease : identifiant pour renouveler ou révoquer un dynamic credential
- Révocation : action réelle, l’user est supprimé de la DB
- Plugins : création_statements dépendent du moteur (PostgreSQL ≠ MongoDB)
- Intégration : l’app doit gérer renouvellement et rechargement des connexions