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 commande sleep 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Ăštre disable_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 :

img.png

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 :

img.png

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.

img.png

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 :

img.png

La console de Vault est maintenant disponible :

img.png

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 :

articleprix/mois
PostgreSQL - Dev0 €
Docker - Plan XS16 €

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