Installation et configuration d'un Vault
Démonstration de configuration et d'utilisation de Vault avec les GitLab Runner¶
Vault peut être utilisé comme gestionnaire de secrets pour les GitLab runner, ce qui signifie que des secrets peuvent êtres stockés dans le Vault et lu par dans les pipelines.
La documentation ci-dessous est une documentation "pratico-théorique" qui va suivre les étapes de la configuration du Vault et de la mise à disposition des secrets dans un pipeline. Elle ne sera pas exhaustive dans toutes les commandes décrites, mais présente la base nécessaire pour utiliser le Vault.
Nous allons configurer un Vault de test et configurer sur ce dernier l'authentification par Json Web Token. L'instance GitLab de production sera utilisée comme acteur de confiance. Une fois cela fait, un pipeline sera configuré sur un projet de test où seront configurés des variables pour un environnement de production et de pré-production.
1 - Configurer le backend kv sur le Vault¶
Dans un premier temps, il faut activer le backend des secrets dans le Vault.
Documentation Vault
sur le chemin /sys/mount
.
Ensuite, effectuer une requête POST sur l'endpoint /sys/mount/<path>
où
<path>
est le chemin sur lequel va être monté le backend kv-2 :
curl --header "X-Vault-Token: $VAULT_TOKEN" --request POST --data'{ "type":"kv-v2" }' $VAULT_ADDR/v1/sys/mounts/<path>
Nous allons, par exemple, monter le backend secret kv-v2 sur le chemin
/sys/mount/secrets
qui seront donc accessibles pour des opérations sur les
secrets sur le chemin /v1/secret/data/<chemin>/<du>/<secret/
:
curl --header "X-Vault-Token: $VAULT_TOKEN" --request POST --data '{ "type":"kv-v2" }' $VAULT_ADDR/v1/sys/mounts/secret
2 - Création des secrets¶
Dans le cas de figure utilisé pour cette documentation, nous avons deux environnements théoriques : la production et la pré-production. Dans chacun de ces environnements, il existe un secret contenant le mot de passe et le nom d'utilisateur de la base de donnée.
Ces secrets seront stockés respectivement dans les chemin /SNUM/DAM/gitlab/gitlab-vault-demo/production/production_database/<production_db_passwd ou production_db_username>
et /SNUM/DAM/gitlab/gitlab-vault-demo/preproduction/preproduction_database/<preproduction_db_passwd ou preproduction_db_username>
Les secrets sont créés via les commandes suivantes :
- Environnement de production :
curl --noproxy "*" --header "X-Vault-Token: $VAULT_TOKEN" --request POST --data '{ "data": {"db-passwd": "Nqd2SFJN1zef2"} }' $VAULT_ADDR/v1/secret/data/SNUM/DAM/gitlab/gitlab-vault-demo/production/production_database/production_db_passwd curl --noproxy "*" --header "X-Vault-Token: $VAULT_TOKEN" --request POST --data '{ "data": {"db-username": "production-database-user"} }' $VAULT_ADDR/v1/secret/data/SNUM/DAM/gitlab/gitlab-vault-demo/production/production_database/production_db_username
- Environnement de pré-production :
curl --noproxy "*" --header "X-Vault-Token: $VAULT_TOKEN" --request POST --data '{ "data": {"db-passwd": "Nqd2SFJN1zef2"} }' $VAULT_ADDR/v1/secret/data/SNUM/DAM/gitlab/gitlab-vault-demo/preproduction/preproduction_database/preproduction_db_passwd curl --noproxy "*" --header "X-Vault-Token: $VAULT_TOKEN" --request POST --data '{ "data": {"db-username": "preproduction-database-user"} }' $VAULT_ADDR/v1/secret/data/SNUM/DAM/gitlab/gitlab-vault-demo/preproduction/preproduction_database/preproduction_db_username
Nous pouvons valider si les secrets ont bien été crées avec le Vault :
vault kv get secret/SNUM/DAM/gitlab/gitlab-vault-demo/preproduction/preproduction_database/preproduction_db_username
vault kv get secret/SNUM/DAM/gitlab/gitlab-vault-demo/preproduction/preproduction_database/preproduction_db_passwd
vault kv get secret/SNUM/DAM/gitlab/gitlab-vault-demo/production/production_database/production_db_passwd
vault kv get secret/SNUM/DAM/gitlab/gitlab-vault-demo/production/production_database/production_db_username
Le test peut aussi être lancé avec une requête HTTP sur l'api du Vault :
curl --noproxy "*" --header "X-Vault-Token: $VAULT_TOKEN" --request GET $VAULT_ADDR/v1/secret/data/SNUM/DAM/gitlab/gitlab-vault-demo/production/production_database/production_db_username
Note : Le dossier /data/
est omis en passant par le Vault car il est
ajouté par ce dernier lors de l'envoi de la requête, néanmoins, le dossier
doit être ajouté si la requête est effectuée manuellement.
3 - Configuration du JWT sur le Vault¶
Il faut ensuite configurer sur le Vault l'authentification par JWT qui sera utilisée par les runners, puis configurer GitLab comme acteur de confiance sur le Vault afin qu'il puisse valider les demandes de token.
Documentation Vault
sur le chemin /auth/jwt
.
Activation de l'authentification par jwt :
curl \
--header "X-Vault-Token: $VAULT_TOKEN" \
--noproxy "*" \
--request POST \
--data '{"type":"jwt","description":"","config":{"options":null,"default_lease_ttl":"0s","max_lease_ttl":"0s","force_no_cache":false},"local":false,"seal_wrap":false,"external_entropy_access":false,"options":null}' $VAULT_ADDR/v1/sys/auth/jwt
Configuration du GitLab comme acteur de confiance :
curl \
--header "X-Vault-Token: $VAULT_TOKEN" \
--noproxy "*" \
--request PUT \
--data '{"bound_issuer":"gitlab-forge.din.developpement-durable.gouv.fr","jwks_url":"https://gitlab-forge.din.developpement-durable.gouv.fr/-/jwks"}' $VAULT_ADDR/v1/auth/jwt/config
4 - Configuration des polices de sécurité et des rôles Vault¶
Une fois les secrets crées et l'authentification JWT activée, il faut créer des polices de sécurité, ainsi que des rôles liés à ces polices.
Note : Lors de la création de police de sécurité, si elles portent sur le backend kv-v2
, il faut ajouter /data/
après le mountpoint du backend.
Par exemple, si le backend secret a été monté sur /secret/
, lors de la
création de police, le bon chemin sera: /secret/data/<chemin_vers_secret>/
,
l'omission de ce dossier dans le chemin d'API entrainera une erreur 403 qui
peut être longue à résoudre.
Création de la police et du rôle pour le secret de pré-production :
Voici le fichier hcl de la police de sécurité pour le secret de pré-production, on y autorise la lecture du secret créé précédemment :
path "/secret/data/SNUM/DAM/gitlab/gitlab-vault-demo/preproduction/*" {
capabilities = [ "read" ]
}
Une fois cette police créée, il faut l'encoder en base64 et la passer dans la commande curl :
export BASE64_POLICY_OUTPUT=$(cat <nom_fichier> > base64 -w 0)
curl --header "X-Vault-Token: $VAULT_TOKEN" --request POST --data '{"policy": "$BASE64_POLICY_OUTPUT"}' $VAULT_ADDR/v1/sys/policies/acl/preproduction-pipeline-policy
Pour finir, il faut créer un rôle qui va utiliser cette police de sécurité, voici le rôle en question :
{
"role_type": "jwt",
"policies": ["preproduction-pipeline-policy"],
"token_explicit_max_ttl": 60,
"user_claim": "user_login",
"bound_claims_type": "glob",
"bound_claims": {
"project_id": "14438",
"ref": "preproduction",
"ref_type": "branch"
}
}
Et la requête pour l'envoyer :
curl --noproxy "*" --header "X-Vault-Token: $VAULT_TOKEN" --request POST --data @preproduction-role.json $VAULT_ADDR/v1/auth/jwt/role/preproduction-role
Création de la police et du rôle pour le secret de production :
Voici le fichier hcl de la police de securité pour le secret de production, on y autorise la lecture du secret créé précédemment :
path "/secrets/data/SNUM/DAM/gitlab/gitlab-vault-demo/production/*" {
capabilities = [ "read" ]
}
Une fois cette police créée, il faut l'encoder en base64 et la passer dans la commande curl :
curl --noproxy "*" --header "X-Vault-Token: $VAULT_TOKEN" --request POST --data '{"policy": "$BASE64_POLICY_OUTPUT"}' $VAULT_ADDR/v1/sys/policies/acl/production-pipeline-policy
Pour finir, il faut créer un rôle qui va utiliser cette police de sécurité, voici le rôle en question :
{
"role_type": "jwt",
"policies": ["production-pipeline-policy"],
"token_explicit_max_ttl": 60,
"user_claim": "user_login",
"bound_claims_type": "glob",
"bound_claims": {
"project_id": "14438",
"ref": ["production"],
"ref_type": "branch"
}
}
Et la requête pour l'envoyer :
curl --noproxy "*" --header "X-Vault-Token: $VAULT_TOKEN" --request POST --data @production-role.json $VAULT_ADDR/v1/auth/jwt/role/production-role
5 - Récupération des secrets via un pipeline GitLab¶
Une fois tous ces élements configurés, il est possible de récupérer les secrets, et pour ce faire, deux méthodes sont supportées :
- Récupérer les secrets dans un stage prévu à cet effet, en utilisant une
image
vault
et en passant les variables d'environnements aux stages qui en ont besoin ; - Récupérer les secrets dans n'importe quel stage, via n'importe quelle image, en effectuant les requêtes HTTP sur l'instance Vault.
Dans ces deux méthodes, un token est récuperé auprès de l'instance Vault, en utilisant le JWT fournit par un pipeline et en précisant le rôle à utiliser.
Ainsi deux variables sont nécessaires au fonctionnement du code :
VAULT_ADDR
: qui est l'adresse du Vault, cette variable ne changera pas et peut donc être configurée en "dur" dans les paramètres du projet ou du groupe ;VAULT_ROLE
: rôle à utiliser pour la demande du token; il peut changer en fonction de la branche ou d'environnement à utiliser.
La variable VAULT_ROLE
peut être construite en fonction de l'environnement, par exemple :
export VAULT_ROLE=${CI_COMMIT_BRANCH}-role
va retourner un rôle différent en fonction de la branche (la variable
CI_COMMIT_BRANCH
étant une variable définie par défaut dans un pipeline).
Les deux méthodes sont décrites ici