
Pour les besoins des cours que je donne Ă l’UniversitĂ© de Lille, j’ai dĂ» configurer un serveur Vault sur Clever Cloud.
Et bien entendu, j’ai fait tout ça avec Terraform.
Cet article dĂ©crit comment utiliser le provider Terraform de Clever Cloud pour dĂ©ployer un serveur Vault. Un article suivant dĂ©crira comment le configurer pour l’authentification OIDC avec GitLab et y stocker quelques secrets Ă titre d’exemple.
Le code de cet article est aussi disponible sur GitHub : https://github.com/juwit/terraform-clevercloud-playground/tree/main/vault.
Cet article a été écrit avec des commandes Terraform, mais fonctionne également avec les commandes OpenTofu équivalentes.
Architecture cible
Avant d’entrer dans la mise en pratique, il convient ici d’expliquer quelques choix illustrĂ©s par le schĂ©ma suivant.
Clever Cloud propose de dĂ©ployer des applications dans de nombreux langages. Pour hĂ©berger une instance Vault, le plus simple semblait d’utiliser une instance Docker.
Par dĂ©fault, Vault propose l’utilisation du backend de stockage Integrated storage pour le stockage des donnĂ©es. Ătant donnĂ© la nature du dĂ©ploiement avec une instance Docker sur un seul nĆud et le fait que Clever Cloud ne supporte pas le stockage persistant pour ce type d’instance, il m’a semblĂ© judicieux d’utiliser un backend de stockage externalisĂ©. Parmi les options proposĂ©es par Vault, 3 options sont envisageables sur Clever Cloud : les bases de donnĂ©es MySQL ou PostgreSQL, ou S3 via l’implĂ©mentation Cellar fournie par Clever Cloud.
Le stockage externalisĂ© sur S3 ne supporte pas la haute disponibilitĂ© et pourrait s’avĂ©rer incompatible avec l’implĂ©mentation Cellar (cf. les adaptations requises par le backend Terraform S3 pour Cellar), donc je l’ai directement Ă©cartĂ© et j’ai privilĂ©giĂ© l’implĂ©mentation avec PostgreSQL.
L’authentification via GitLab permet Ă mes Ă©tudiants d’utiliser leur compte GitLab existant, en exploitant l’instance GitLab fournie par l’UniversitĂ© de Lille. C’est donc trĂšs pratique pour eux (pas besoin d’avoir un compte ailleurs) et pour moi (pas besoin de crĂ©er et de fournir des comptes). J’aurais aussi pu utiliser une instance KeyCloak pour implĂ©menter l’authentification, mais cela aurait complexifiĂ© inutilement l’implĂ©mentation.
Ă noter aussi que je ne suis pas expert Vault, donc je ne suis pas Ă l’abri d’avoir fait une erreur de configuration quelque part, alors attention si vous utilisez cette configuration en production đ
SetUp de Terraform
Dans un article prĂ©cĂ©dent, j’ai dĂ©jĂ expliquĂ© comment configurer Terraform pour Clever Cloud, ainsi que comment configurer un backend via un bucket Cellar. Ces Ă©tapes ne sont pas dĂ©crites ici pour ne pas alourdir cet article, mais sont bien nĂ©cessaires.
Création de la base de données avec Terraform
La premiÚre étape consiste à créer une base de données consacrée à Vault. Avec Terraform, la création de la base de données se fait avec le code suivant :
resource "clevercloud_postgresql" "vault_storage" {
name = "vault_storage"
plan = "dev"
region = "par"
}
Vault nĂ©cessite que le schĂ©ma de la base de donnĂ©es soit initialisĂ© avant que l’application ne soit dĂ©marrĂ©e. Le schĂ©ma est fourni dans la documentation du backend :
CREATE TABLE vault_kv_store (
parent_path TEXT COLLATE "C" NOT NULL,
path TEXT COLLATE "C",
key TEXT COLLATE "C",
value BYTEA,
CONSTRAINT pkey PRIMARY KEY (path, key)
);
CREATE INDEX parent_path_idx ON vault_kv_store (parent_path);
Ce script peut ĂȘtre passĂ© Ă la main via psql
, ou dans la console Clever Cloud.
Il est aussi possible d’utiliser un provisioner Terraform pour exĂ©cuter le script aprĂšs la crĂ©ation de la base de donnĂ©es :
resource "clevercloud_postgresql" "vault_storage" {
name = "vault_storage"
plan = "dev"
region = "par"
provisioner "local-exec" {
# wait for the database to be up
command = "sleep 10 && psql -f vault-schema.sql"
environment = {
PGHOST = self.host
PGPORT = self.port
PGDATABASE = self.database
PGUSER = self.user
PGPASSWORD = self.password
}
}
}
Ici, le provisioner local-exec
est utilisé pour exécuter la commande psql
aprÚs avoir attendu quelques secondes, le temps que la base de données soit effectivement créée.
Les variables d’environnement nĂ©cessaires Ă l’exĂ©cution de psql
sont également positionnées.
Je ne suis pas un grand fan de l’exĂ©cution de provisioners, car ils impliquent une dĂ©pendance avec la machine qui exĂ©cute Terraform. Ici, c’est le binaire
psql
et la commandesleep
dans le script shell qui sont nécessaires.
CrĂ©ation de l’instance Vault avec Terraform
Une fois la base de donnĂ©es crĂ©Ă©e et le schĂ©ma initialisĂ©, on peut crĂ©er l’instance Docker pour notre Vault sur Clever Cloud avec le code suivant :
resource "clevercloud_docker" "vault_instance" {
name = "vault_instance"
# vertical auto-scaling disabled
smallest_flavor = "XS"
biggest_flavor = "XS"
# horizontal auto-scaling disabled
min_instance_count = 1
max_instance_count = 1
# network setup
additional_vhosts = ["vault-instance.cleverapps.io"]
redirect_https = true
# URL for the storage backend
environment = {
VAULT_LOCAL_CONFIG = jsonencode(
{
"storage"Â : {
"postgresql"Â : {
"connection_url"Â : "postgres://${clevercloud_postgresql.vault_storage.user}:${clevercloud_postgresql.vault_storage.password}@${clevercloud_postgresql.vault_storage.host}:${clevercloud_postgresql.vault_storage.port}/${clevercloud_postgresql.vault_storage.database}"
}
},
"listener"Â : [{ "tcp"Â : { "address"Â : "0.0.0.0:8080", "tls_disable"Â : true } }],
"disable_mlock"Â : true,
"ui"Â : true
})
}
}
Parmi les paramÚtres de configuration intéressants, on retrouve les paramÚtres principaux de la ressource clevercloud_docker
, avec les paramĂštres de scalabilitĂ© horizontale et verticale, ainsi que la dĂ©claration d’un nom de domaine customisĂ©.
Les variables d’environnement permettent de passer sa configuration Ă Vault (plutĂŽt que d’utiliser un fichier).
C’est un des aspects bien pratique de l’image Docker de Vault (documentĂ© sur dockerhub).
Ici, on utilise la variable VAULT_LOCAL_CONFIG
, dans laquelle on donne du contenu formatĂ© en JSON, Ă l’aide de la fonction Terraform jsonencode()
.
Concernant la configuration de Vault, le stockage sur l’instance PostgreSQL est dĂ©fini Ă travers le paramĂštre "storage" : { "postgresql" : {} }
. L’URL de connexion est passĂ©e en paramĂštre, elle est reconstruite Ă partir des attributs de la ressource clevercloud_postgresql.vault_storage
. Le paramĂštre listener
permet de forcer Vault Ă Ă©couter sur le port 8080
, à la place du port par défaut 8200
, qui est le port d’Ă©coute attendu par Clever Cloud. L’utilisation de l’adresse 0.0.0.0
permet aussi d’Ă©couter sur les connexions provenant d’internet (Ă la place de l’adresse localhost 127.0.0.1
par dĂ©faut). C’est aussi Clever Cloud qui va s’occuper de l’exposition d’un certificat pour l’accĂšs en HTTPS Ă l’instance, on dĂ©sactive donc le TLS avec l’option tls_disable
.
Enfin, on désactive le lock de mémoire en RAM avec disable_mlock
, car l’exĂ©cution de containers Docker sur Clever Cloud ne permet pas, Ă ma connaissance, l’utilisation de la capability Linux IPC_LOCK
. Cette capability de Linux permet de donner les droits à un processus de verrouiller sa mémoire en RAM pour éviter que la mémoire soit écrite sur le swap. Le paramÚtre ui
permet d’activer la console graphique de Vault, qui sera bien pratique pour les Ă©tapes suivantes.
Une fois l’application Docker crĂ©Ă©e, on peut rĂ©cupĂ©rer son identifiant Clever Cloud avec un output Terraform :
output "vault_instance_id" {
description = "Clever Cloud id for the instance. Use with `clever link` before deploying."
value = clevercloud_docker.vault_instance.id
}
$ terraform output -raw vault_instance_id
app_72d4b5a4-1ab8-4653-a825-9be0c62e0fa1
Cet output permettra d’exĂ©cuter les commande clever link
et clever deploy
pour dĂ©ployer l’instance Vault Ă l’Ă©tape suivante.
DĂ©ploiement de Vault
Le dĂ©ploiement d’une application Docker sur Clever Cloud passe par l’Ă©criture d’un Dockerfile
et l’exĂ©cution de la commande clever deploy
.
Le contenu du fichier Dockerfile
est simpliste :
FROM hashicorp/vault:1.18
CMD ["server"]
On part d’une version fixĂ©e de Vault, (la version 1.18 Ă©tant la plus rĂ©cente Ă l’heure de l’Ă©criture de ces lignes), et on surcharge la commande exĂ©cutĂ©e par Vault au dĂ©marrage de l’application avec la directive CMD ["server"]
.
Par défaut, Vault démarre en mode « développement », avec la commande CMD ["server", "-dev"]
. Si vous souhaitez simplifier vos tests, vous pouvez conserver cette directive, mais elle est dĂ©conseillĂ©e pour de la production. Je l’ai donc dĂ©sactivĂ©e dans cet article.
AprÚs avoir créé un repository Git pour notre fichier et commité celui-ci, le déploiement se fait en 2 commandes, clever link
pour associer le repository Git courant Ă l’instance Clever Cloud Docker, puis clever deploy
pour soumettre le code source à Clever Cloud :
$ clever link app_72d4b5a4-1ab8-4653-a825-9be0c62e0fa1
Your application has been successfully linked!
$ clever deploy
Remote application is app_id=app_72d4b5a4-1ab8-4653-a825-9be0c62e0fa1, alias=vault_instance, name=vault_instance
Remote application belongs to orga_0331b635-5a61-4786-8f2f-dee81a1b8970
App is brand new, no commits on remote yet
New local commit to push is c6eb36c12ee5ca4a6f0cbcaa2683310856ef7f42 (from refs/heads/main)
Pushing source code to Clever Cloud
Your source code has been pushed to Clever Cloud.
Waiting for deployment to start
Deployment started (deployment_f5deb5ec-e9af-4f19-a5c2-978356632954)
Waiting for application logs
Couldn't start vault with IPC_LOCK. Disabling IPC_LOCK, please use --cap-add IPC_LOCK
==> Vault server configuration:
Administrative Namespace:
Cgo: disabled
Environment Variables: APP_HOME, APP_ID, CC_APP_ID, CC_APP_NAME, CC_COMMIT_ID, CC_DEPLOYMENT_ID, CC_ENVIRON_UPDATE_TOKEN, CC_ENVIRON_UPDATE_URL, CC_INSTANCE_ID, CC_OWNER_ID, CC_PRETTY_INSTANCE_NAME, CC_REVERSE_PROXY_IPS, CC_USE_PULSAR_LOGSCOLLECTION, COMMIT_ID, HOME, HOSTNAME, INSTANCE_ID, INSTANCE_NUMBER, INSTANCE_TYPE, NAME, PATH, PORT, PWD, SHLVL, VAULT_LOCAL_CONFIG, VAULT_PG_CONNECTION_URL, VERSION
Go Version: go1.23.3
Listener 1: tcp (addr: "0.0.0.0:8080", cluster address: "0.0.0.0:8081", disable_request_limiter: "false", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled")
Log Level:
Mlock: supported: true, enabled: false
Recovery Mode: false
Storage: postgresql (HA disabled)
Version: Vault v1.18.3, built 2024-12-16T14:00:53Z
Version Sha: 7ae4eca5403bf574f142cd8f987b8d83bafcd1de
2025-01-03T14:25:52.301Z [INFO] proxy environment: http_proxy="" https_proxy="" no_proxy=""
2025-01-03T14:25:52.333Z [INFO] incrementing seal generation: generation=1
2025-01-03T14:25:52.333Z [WARN] no `api_addr` value specified in config or in VAULT_API_ADDR; falling back to detection if possible, but this value should be manually set
2025-01-03T14:25:52.336Z [INFO] core: Initializing version history cache for core
2025-01-03T14:25:52.336Z [INFO] events: Starting event system
==> Vault server started! Log data will stream in below:
Application start successful
Successfully deployed in 0 minutes and 28 seconds
Au démarrage, Vault indique que la configuration est bien chargée, et affiche quelques warnings.
Concernant le warning mentionnant l’IPC_LOCK, il n’est pas possible Ă ma connaissance de forcer l’option
--cap-add IPC_LOCK
sur Clever Cloud. Néanmoins, ce warning ne pose pas de problÚme, puisque le lock de la mémoire est désactivé avec le paramÚtredisable_mlock
.
Une fois le démarrage terminé, la commande clever open
permet d’ouvrir un navigateur web sur notre instance de Vault !
$ clever open
Opening the application in your browser
L’URL d’accĂšs Ă Vault peut aussi ĂȘtre rĂ©cupĂ©rĂ©e de deux maniĂšres : avec la commande clever domain
, ou avec un output Terraform qu’on ajoute au code qui crĂ©e l’instance Docker.
$ clever domain
app_72d4b5a4-1ab8-4653-a825-9be0c62e0fa1.cleverapps.io
* vault-instance.cleverapps.io
output "vault_url" {
description = "URL of the Vault instance."
value = clevercloud_docker.vault_instance.vhost
}
$ terraform output -raw vault_url
app_72d4b5a4-1ab8-4653-a825-9be0c62e0fa1.cleverapps.io
Initialisation du Vault
Lors de sa premiĂšre ouverture, Vault doit ĂȘtre initialisĂ©, puis dĂ©verrouillĂ©. Ces Ă©tapes permettent de crĂ©er ses clĂ©s de dĂ©verrouillage (unseal keys), ainsi que le token d’accĂšs root
qui permettra d’utiliser l’API dans un premier temps.
Ces opĂ©rations doivent ĂȘtre faites une seule fois Ă la crĂ©ation du serveur Vault et doivent ĂȘtre faites manuellement via le CLI Vault ou sa console. Dans cet exemple, nous allons effectuer ces manipulations dans la console de Vault :
Une fois le nombre de clĂ©s choisi, ainsi que les diffĂ©rentes options de chiffrement, Vault gĂ©nĂšre les clĂ©s et les met Ă disposition sur l’Ă©cran suivant :
Ces clĂ©s ne doivent ĂȘtre perdues en aucune circonstance ! En cas d’utilisation en production, le nombre de clĂ©s souhaitĂ© sera probablement diffĂ©rent de 1 !
AprĂšs avoir stockĂ© les clĂ©s en lieu sĂ»r, l’Ă©cran suivant nous invite Ă dĂ©verrouiller Vault en saisissant une clĂ© de dĂ©verrouillage. Lorsque suffisamment de clĂ©s auront Ă©tĂ© entrĂ©es, Vault sera dĂ©verrouillĂ© et prĂȘt Ă l’utilisation.
Une fois Vault dĂ©verrouillĂ©, l’Ă©cran de login apparaĂźt, il est alors possible de se connecter avec le token d’accĂšs root
obtenu aux étapes précédentes :
La console de Vault est maintenant disponible :
Vault est maintenant initialisĂ©, dĂ©verrouillĂ© et prĂȘt Ă ĂȘtre utilisĂ©Â !
L’article suivant traitera de la configuration de Vault pour utiliser l’authentification OIDC de GitLab, et finaliser cette architecture.
En conclusion
Cet article a prĂ©sentĂ© comment mettre en Ćuvre l’installation et la configuration d’un serveur Vault sur Clever Cloud.
C’est cette infrastructure qui m’a permis de pouvoir mettre Ă disposition rapidement un serveur Vault pour mes Ă©tudiants, afin de les former Ă la rĂ©cupĂ©ration de secrets depuis une application Spring Boot.
Pour exĂ©cuter l’infrastructure proposĂ©e dans cet article, il vous en coĂ»tera environ 16 âŹ/mois avec les plans utilisĂ©s :
article | prix/mois |
---|---|
PostgreSQL - Dev | 0 ⏠|
Docker - Plan XS | 16 ⏠|
Cette architecture n’est pas parfaite, mais permet de facilement dĂ©ployer un Vault pour des cas d’usage simples ou un environnement de dev. Il faudrait bien entendu la revoir (en particulier les plans utilisĂ©s) pour un environnement de production.
Liens et références
- Exemples de code de cet article sur GitHub
- Page d’accueil de Clever Cloud
- Installation du CLI Clever Cloud
- Installation du CLI Terraform
- Installation du CLI OpenTofu
- Documentation du provider Terraform Clever Cloud :
- Ressource
clevercloud_postgresql
- Ressource
clevercloud_docker
- Ressource
- Documentation de Vault :
- L’image Docker de Vault sur dockerhub
- Configuration du storage PostgreSQL
- IPC_LOCK et mlock :
- Manpage des capabilities Linux (pour l’option
--cap-add IPC_LOCK
) - Manpage de l’appel systĂšme mlock
- Un article Vault and mlock() dans le help center de HashiCorp
- Le paramĂštre
disable_mlock
dans la configuration de Vault
- Manpage des capabilities Linux (pour l’option
- Photo de couverture par Jason Dent sur Unsplash