Aller au contenu

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><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 :

  1. 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
    
  2. 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 :

  1. 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 ;
  2. 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