Ce projet montre qu'une application Python peut recuperer un mot de passe PostgreSQL depuis Vault au demarrage, sans stocker ce mot de passe dans le code, dans Git ou dans un fichier local.
Dans beaucoup de projets, les secrets finissent dans un .env, un docker-compose.yml ou directement dans le code. Des qu'un secret est duplique a plusieurs endroits, il devient difficile a proteger et a revoquer.
Le projet suit un flux tres simple:
vault.pyprepare Vault et stocke le mot de passe de la base.vault.pycree un acces applicatif avec AppRole.app.pyrecupere le secret au demarrage, se connecte a PostgreSQL et garde le mot de passe uniquement en memoire.
Ce projet utilise le mode AppRole de Vault, et non le mode userpass.
userpasssert plutot a authentifier un utilisateur humain avec un login et un mot de passe.AppRolesert plutot a authentifier une application ou une machine.
Dans ce projet, AppRole est plus adapte car l'objectif est de faire authentifier une application Python automatiquement au demarrage.
Le fonctionnement est volontairement simple:
vault.pycree un role AppRole pour l'application.- Vault genere un
role_id. - Vault genere aussi un
secret_id, mais il est transmis sous forme dewrapped tokenpour eviter de l'exposer directement. app.pyouvre d'abord ce wrapped token pour recuperer lesecret_id.- Ensuite,
app.pyenvoierole_id + secret_ida Vault. - Vault renvoie alors un token temporaire limite par la policy
app-read-db. - Avec ce token temporaire, l'application peut lire uniquement
secret/app/db.
En resume:
role_id= identifiant de l'applicationsecret_id= secret d'authentification de l'application- token Vault = acces temporaire donne par Vault apres verification
L'application n'utilise donc jamais le root token pour lire les secrets.
flowchart LR
A[app.py] -->|unwrap du wrapped token| B[Vault]
B -->|retourne secret_id| A
A -->|role_id + secret_id| B
B -->|retourne un token temporaire| A
A -->|lit secret/app/db| B
B -->|retourne le mot de passe DB| A
A -->|connexion avec le secret en memoire| C[PostgreSQL]
Projet_Vault/
├── app.py
├── vault.py
├── docker-compose.yml
├── requirements.txt
└── vault/
├── vault-config.hcl
└── app-policy.hcl
docker-compose.ymllance Vault et PostgreSQL en local.vault.pyconfigure Vault et prepare l'acces de l'application.app.pysimule une application cliente qui lit son secret au demarrage.vault/app-policy.hcllimite l'application a la lecture de son secret.vault/vault-config.hclcontient la configuration locale du serveur Vault.
cd C:\repertoire-projet
python -m pip install -r requirements.txt
docker compose up -d
python vault.pycd ~/repertoire-projet
python -m pip install -r requirements.txt
docker compose up -d
python vault.pyAu premier lancement, conserve bien:
VAULT_UNSEAL_KEYVAULT_ROOT_TOKENVAULT_ROLE_IDVAULT_WRAPPED_SECRET_ID
$env:VAULT_ADDR='http://127.0.0.1:8200'
$env:VAULT_ROLE_ID='COLLER_LE_ROLE_ID'
$env:VAULT_WRAPPED_SECRET_ID='COLLER_LE_WRAPPED_SECRET_ID'
python app.pyexport VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_ROLE_ID='COLLER_LE_ROLE_ID'
export VAULT_WRAPPED_SECRET_ID='COLLER_LE_WRAPPED_SECRET_ID'
python app.pyVault se rescelle apres redemarrage. Il faut donc remettre la cle d'unseal et le root token avant de relancer vault.py.
$env:VAULT_UNSEAL_KEY='COLLER_LA_CLE_UNSEAL'
$env:VAULT_ROOT_TOKEN='COLLER_LE_ROOT_TOKEN'
python vault.pyexport VAULT_UNSEAL_KEY='COLLER_LA_CLE_UNSEAL'
export VAULT_ROOT_TOKEN='COLLER_LE_ROOT_TOKEN'
python vault.pycd C:\repertoire-projetpython -m pip install -r requirements.txtdocker compose up -d
docker compose psTu dois voir vault_server et postgres_container en cours d'execution.
Note: PostgreSQL est expose sur le port 5433 pour eviter un conflit avec un PostgreSQL local deja present sur la machine.
python vault.pyLe script affiche:
VAULT_UNSEAL_KEYetVAULT_ROOT_TOKENau premier lancement,VAULT_ROLE_ID,VAULT_WRAPPED_SECRET_ID.
Si Vault a deja ete initialise et que le conteneur a redemarre, il faut d'abord definir VAULT_UNSEAL_KEY et VAULT_ROOT_TOKEN, puis relancer python vault.py.
$env:VAULT_ADDR='http://127.0.0.1:8200'
$env:VAULT_ROLE_ID='COLLER_LE_ROLE_ID'
$env:VAULT_WRAPPED_SECRET_ID='COLLER_LE_WRAPPED_SECRET_ID'
python app.pyL'application doit:
- ouvrir le wrapped token,
- se connecter a Vault avec AppRole,
- lire le secret dans Vault,
- se connecter a PostgreSQL,
- afficher la ligne de demo lue dans la base.
Exemple de sortie:
Connexion OK a la base 'db_vault' avec l'utilisateur 'vault_app'.
Ligne de demo lue en base : secret recupere depuis Vault au demarrage
Le VAULT_WRAPPED_SECRET_ID est temporaire. Pour en generer un nouveau:
python vault.pydocker compose downPour tout supprimer, y compris les volumes:
docker compose down -v- aucun mot de passe n'est ecrit en dur dans le repo,
- l'application n'utilise pas le root token,
- l'acces Vault est limite a un seul secret,
- le mot de passe est utilise uniquement en memoire.
- pourquoi
AppRoleest plus adapte qu'un simple login/mot de passe pour une application, - comment limiter l'acces a un secret avec une policy Vault,
- comment transmettre un
secret_idde maniere plus propre avec un wrapped token, - pourquoi il faut penser aux conflits de ports et aux volumes persistants dans Docker,
- comment garder un secret uniquement en memoire dans une application.
Ce projet est sous licence MIT — voir le fichier LICENSE pour plus de détails.
