Déployer un cluster Vault haute disponibilité en 9 minutes

Benoît Garçon on 2023-02-03

© Image par HashiCorp

Cet article vous est proposé par Benoît Garçon et Loïc Le Reun consultants chez Les Filles et Les Garçons de la Tech.

Lorsqu’il s’agit de stocker et de gérer les informations sensibles telles que les mots de passe et les certificats d’authentification, la sécurité est primordiale. Pour garantir un niveau élevé de sécurité, il est important de déployer un système de stockage de clés hautement disponible et fiable. Vault by HashiCorp est une solution populaire pour ce genre de tâches.

Dans cet article, nous allons vous montrer comment déployer un cluster Vault hautement disponible avec le stockage intégré Raft et une fonction d’auto-décèlement. Nous déploierons le cluster Vault sur un cluster Kubernetes en utilisant Kind et Calico pour la gestion des réseaux. Le cluster Vault sera composé de cinq instances pour assurer une tolérance aux pannes élevée, et nous utiliserons un cluster de transit pour effectuer le décèlement automatique.

Il est important de noter que chaque nœud doit avoir une seule instance de Vault. Cela garantit la conformité aux pratiques recommandées en matière de sécurité, ainsi que la sécurité du stockage de clés en général.

L’installation et la gestion d’un cluster Vault haute disponibilité peut s’avérer complexe et fastidieuse. Cependant, grâce à l’utilisation de l’operator Bank-Vaults de Banzaicloud, ce processus est grandement simplifié.

En suivant les étapes décrites dans cet article, vous pouvez déployer un cluster Vault hautement disponible et sécurisé, prêt à stocker et à gérer vos informations sensibles avec confiance.

Architecture d’un cluster Vault HA avec décèlement automatique sur Kubernetes

L’ensemble des sources relatives à cet article sont disponibles sur GitHub : https://github.com/Les-filles-et-les-garcons-de-la-tech/vault-k8s. N’hésitez pas à ouvrir des discussions dans la section issues pour partager vos questions et remarques.

Prérequis

Pour déployer Vault sur Kubernetes, vous aurez besoin des outils kubectl and helm.

Sur un système type Ubuntu, exécutez les commandes suivantes pour installer kubectl :

su - # Les droits root sont nécessaires, l'utilisation de sudo est possible
apt-get update -y && apt-get install -y ca-certificates curl
curl -fsSLo /etc/apt/keyrings/kubernetes-archive-keyring.gpg \
            https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] \
https://apt.kubernetes.io/ kubernetes-xenial main" \
| tee /etc/apt/sources.list.d/kubernetes.list
apt-get update -y && apt-get install -y kubectl
exit # Quitter la session root

Exécutez les lignes suivantes pour installer la ligne de commande helm:

su - # Les droits root sont nécessaires, l'utilisation de sudo est possible
apt-get update -y && apt-get install -y curl gpg
curl -O https://baltocdn.com/helm/signing.asc
gpg --dearmor -o /usr/share/keyrings/helm.gpg signing.asc
apt-get install -y apt-transport-https
echo "deb [arch=`dpkg --print-architecture` \
signed-by=/usr/share/keyrings/helm.gpg] \
https://baltocdn.com/helm/stable/debian/ all main" \
| tee /etc/apt/sources.list.d/helm-stable-debian.list
apt-get update -y && apt-get install -y helm
exit # Quitter la session root

Optionnel : Installer un cluster Kind avec Calico

Si vous n’avez pas de cluster Kubernetes à disposition pour installer Vault, voici quelques indications pour monter votre propre lab.

La première chose est d’installer Kind. Vous pouvez suivre la documentation officielle adaptée à votre environnement. Par exemple, pour un système Linux il est possible d’installer Kind à partir des binaires comme suit :

curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.17.0/kind-linux-amd64
chmod +x ./kind
su - # Les droits root sont nécessaires, l'utilisation de sudo est possible
mv ./kind /usr/local/bin/kind
exit # Quitter la session root

A ce stade un simple appel à la commande kind version devrait afficher… la version de kind.

Nous allons maintenant définir un fichier kind-config.yaml permettant de configurer le contenu du cluster (control plane, worker, etc.).

Voici un exemple de configuration qui nous permettra d’obtenir un control plane et 5 workers afin d’héberger les 6 instances de Vault sur des nodes indépendants.

cat <<EOT >> kind-config.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
networking:
  disableDefaultCNI: true
  podSubnet: 192.168.0.0/16
nodes:
- role: control-plane
  image: kindest/node:v1.24.7@sha256:577c630ce8e509131eab1aea12c022190978dd2f745aac5eb1fe65c0807eb315
- role: worker
  image: kindest/node:v1.24.7@sha256:577c630ce8e509131eab1aea12c022190978dd2f745aac5eb1fe65c0807eb315
- role: worker
  image: kindest/node:v1.24.7@sha256:577c630ce8e509131eab1aea12c022190978dd2f745aac5eb1fe65c0807eb315
- role: worker
  image: kindest/node:v1.24.7@sha256:577c630ce8e509131eab1aea12c022190978dd2f745aac5eb1fe65c0807eb315
- role: worker
  image: kindest/node:v1.24.7@sha256:577c630ce8e509131eab1aea12c022190978dd2f745aac5eb1fe65c0807eb315
- role: worker
  image: kindest/node:v1.24.7@sha256:577c630ce8e509131eab1aea12c022190978dd2f745aac5eb1fe65c0807eb315
EOT

Enfin en exécutant les commandes suivantes, vous allez instancier le cluster défini au dessus, ajouter un operator pour gérer Calico et déployer Calico lui-même.

# Création du cluster kind utilisant le fichier de configuration précédent
kind create cluster --name kind-cluster --config kind-config.yaml
# Permettre à la 6ème instance de Vault et calico de se déployer sur le control plane
kubectl taint nodes --all node-role.kubernetes.io/control-plane:NoSchedule-
# Installation de l'operator Tigera pour Calico
kubectl create -f https://projectcalico.docs.tigera.io/archive/v3.22/manifests/tigera-operator.yaml
# Installation de l'operator de Calico
kubectl create -f https://projectcalico.docs.tigera.io/archive/v3.22/manifests/custom-resources.yaml

Après un peu d’attente, vous devriez obtenir un résultat similaire en exécutant un kubectl get nodes :

kubectl get nodes
NAME                         STATUS   ROLES           AGE   VERSION
kind-cluster-control-plane   Ready    control-plane   9d    v1.25.3
kind-cluster-worker          Ready    <none>          9d    v1.25.3
kind-cluster-worker2         Ready    <none>          9d    v1.25.3
kind-cluster-worker3         Ready    <none>          9d    v1.25.3
kind-cluster-worker4         Ready    <none>          9d    v1.25.3
kind-cluster-worker5         Ready    <none>          9d    v1.25.3

Étape 1 : Installation de l’opérateur Banzaicloud

L’utilisation de l’opérateur Vault de Banzaicloud pour gérer les instances Vault ajoute une couche supplémentaire de simplicité et de fiabilité à ce déploiement de haute disponibilité. Avec cet opérateur, les tâches liées à la gestion des instances Vault sont automatisées, ce qui permet de se concentrer sur d’autres aspects de la mise en œuvre de l’architecture.

La première étape consiste à ajouter les dépôts Helm où se trouve le chart officiel et ses dépendances.

helm repo add banzaicloud-stable https://kubernetes-charts.banzaicloud.com
helm repo add jetstack https://charts.jetstack.io

Les dépôts doivent ensuite être mis à jour :

helm repo update

L’ensemble du code source nécessaire à l’application de ce guide est disponible sur le GitHub de FGTech, c’est donc de là que nous allons télécharger le code :

git clone https://github.com/Les-filles-et-les-garcons-de-la-tech/vault-k8s

Vous pouvez alors installer le chart du vault-operator :

helm upgrade --install -f helm/banzaicloud-values.yaml vault-operator \
--create-namespace -n vault-operator \
banzaicloud-stable/vault-operator --version 1.16.1

Ensuite, c’est au tour du secret webhook Vault :

helm upgrade --install -f helm/banzaicloud-values.yaml vault-secret-webhook \
--create-namespace -n vault-operator \
banzaicloud-stable/vault-secrets-webhook --version 1.16.0

Il sera simple de vérifier le bon déploiement de l’opérateur et du secret webhook avec la commande suivante.

kubectl get pods -n vault-operator
NAME                                                      READY  STATUS RESTARTS AGE
vault-operator-75d4444b79-h57fq                             1/1 Running        0  1m
vault-secret-webhook-vault-secrets-webhook-78fc978df9–7k527 1/1 Running        0  1m
vault-secret-webhook-vault-secrets-webhook-78fc978df9-bltdc 1/1 Running        0  1m

Tout notre environnement est prêt, il est temps de déployer Vault à proprement parlé.

Étape 2 : Sécurisation des namespaces

Créons d’abord les namespaces nécessaires pour héberger les instances de Vault. Dans notre cas nous les appelons : transit-vault et ha-vault.

kubectl apply -f ./vault/namespaces.yaml

Ensuite nous allons déployer les network policies nécessaires au durcissement de nos trois namespaces. Les flux à autoriser correspondent à ceux décrits dans le schéma en début d’article.

kubectl apply -f ./vault/network-policies/

Étape 3 : Déploiement du Vault pour décèlement automatique

Le déploiement de l’instance de transit se fait alors en déployant les manifestes contenus dans le dossier ./vault/transit-vault/.

kubectl apply -f ./vault/transit-vault/

Enfin pour vérifier que tout fonctionne correctement, affichez le status des pods du Vault de transit.

kubectl get pods -n transit-vault
NAME                                        READY   STATUS   RESTARTS  AGE
transit-vault-0                             3/3     Running  0         1m
transit-vault-configurer-5ccb945cc4-gp7cw   1/1     Running  0         1m

Étape 4 : Déploiement du cluster Vault haute disponibilité (HA)

Le déploiement du cluster des 5 instances Vault se fait en appliquant les manifestes disponibles dans le dossier vault/vault-ha-raft/.

kubectl apply -f ./vault/ha-vault/

La vérification du status du cluster peut une nouvelle fois se faire en vérifiant l’état des pods.

kubectl get pods -n ha-vault
NAME                                  READY STATUS   RESTARTS  AGE
ha-vault-0                            3/3   Running  0         5m
ha-vault-1                            3/3   Running  0         4m
ha-vault-2                            3/3   Running  0         3m
ha-vault-3                            3/3   Running  0         2m
ha-vault-4                            3/3   Running  0         1m
ha-vault-configurer-7c66c8bf69–58vhn  1/1   Running  0         5m

Vous disposez maintenant d’un cluster opérationnel à haute disponibilité.

Étape 5 : Sécurisation du système

Dans le système mis en place précédemment certains aspects sécurité restent toutefois ouverts et demandent une attention particulière de la part des administrateurs. En particulier, il est important de s’intéresser aux secrets Kubernetes, aux network policies et aux policies Vault.

En premier lieu, un secret particulier a été créé dans le namespace ha-vault. Il sagit du secret ha-vault ha-vault-unseal-keys.

kubectl get secrets -n ha-vault ha-vault-unseal-keys
NAME                   TYPE     DATA   AGE
ha-vault-unseal-keys   Opaque   7      4m

Ce secret contient les clés de décèlement du cluster ainsi que le token du root. Il est important de récupérer ces secrets et de les changer en organisant une cérémonie des clés lors de laquelle de nouvelles clés seront générées et chaque acteur de confiance se verra remettre un fragment de clé que lui seul connaîtra. Ainsi les clés de décèlement effectives n’auront jamais été enregistrées dans Kubernetes.

Pour récupérer les clés il suffit d’afficher le contenu du secret ha-vault-unseal-keys.

kubectl get secrets -n ha-vault ha-vault-unseal-keys -o yaml
apiVersion: v1
data:
  vault-recovery-0: MjM1ZjllYWI2ZWViMWNBNDRlNjYwNWM0MjkzMDNhNzcxNjkyYzlmNWVkYzgxN2M1ODI0MDYyYjcyOTA2ZGNlNzNj
  vault-recovery-1: MDViNTJiZTIwMjByZjY5MTdhNWM2NjRkZTY1NWE2MmQyN2ViNTgyMzBlMGZmZTExN2UyMDQ0NTJhNzA0OTRhMDFk
  vault-recovery-2: MTcxMjRjOTkzNzZlNTM1ZTczYjMyOTBhNGQ2M2U4MDBhM2Y0NjVhMzg5ZjFlMzY0ODYzYWQ4Zjg4NzcyMzViY2Ez
  vault-recovery-3: MjM5YWE1YzQ0M2RjY2VBMTIyYzc1YjYwNmYwZDIyOTVkYmY5ZTUxMGMyMGFhM2E3ZjI4NmIwYmJmMGE3ZTI3NGVk
  vault-recovery-4: NjIxNTYzNmE1NjNBZmU1MzQ3NjcwNjYxZWU3MzM1ZDFiYzkyY2I1MDRlMDYxN2FkZTQzOWY1ZjRmNDk0MTFkOTEw
  vault-root: aHZzLk94ZnBqTG94QjFIS1VTblVSU0o1bmdHRQ==
...

Les network policies proposées permettent quant à elles d’isoler Vault au maximum. Vous les trouverez dans le dossier vault/network-policies. Afin qu’une application puisse communiquer avec Vault et donc répondre aux exigences des network policies il faudra ajouter un label particulier que nous avons défini dans le fichier vault/network-policies/pseudo-vault-network-policy.yaml:L38.

vault_access: "true"

En labelisant ainsi vos pods applicatifs ils répondront aux règles des policies et pourront requêter Vault sur son port 8200.

La section metadata de vos pods devrait ainsi contenir ces lignes.

metadata:
  labels:
    vault_access: "true"
  annotations:
    vault.security.banzaicloud.io/vault-addr: "https://ha-vault.ha-vault:8200"
    vault.security.banzaicloud.io/vault-role: "app"
    vault.security.banzaicloud.io/vault-path: "app/kubernetes"

Les annotations permettent à votre pod d’accéder à Vault via l’authentification Kubernetes via les paths et rôles définis dans le fichier ha-vault/ha-vault.yaml.

Étape 6 : Accès à l’UI web de Vault

Si le root token par défaut n’a pas encore été révoqué, vous pouvez l’utiliser pour accéder à l’interface web. Commençons d’abord par rendre accessible l’application sur le port (par défaut) 8200 de votre machine.

kubectl port-forward -n ha-vault svc/ha-vault 8200:8200

La sortie standard devrait afficher une sortie similaire à la suivante.

Forwarding from 127.0.0.1:8200 -> 8200
Forwarding from [::1]:8200 -> 8200

Il suffit ensuite de se rendre à l’adresse https://localhost:8200/ via un navigateur web.

Page de connexion à Vault

Cette démonstration intègre aussi une préconfiguration pour intégrer une connexion via LDAP (cf vault/ha-vault/ha-vault.yaml:L369), mais dans cet article nous nous limiterons à nous connecter via token en nous rendons dans l’onglet Other puis en sélectionnant la méthode Token.

Il est possible de récupérer le token du user root dans le secret ha-vault-unseal-keys puis de l’utiliser dans le champ Token.

kubectl get secrets -n ha-vault ha-vault-unseal-keys \
  -o jsonpath={.data.vault-root} | base64 --decode | more

Vous accéderez ainsi à l’interface de Vault contenant les secret engines pré-configurés à partir de la ligne 398 du fichier vault/ha-vault/ha-vault.yaml.

Ecran d’accueil de Vault

Conclusion

Le déploiement d’un cluster Vault haute disponibilité avec stockage intégré Raft et auto-décèlement est un processus important pour garantir la sécurité et la fiabilité de vos secrets. En utilisant cette architecture, vous pouvez être sûr que vos secrets seront stockés en toute sécurité et disponibles en tout temps, même en cas de panne d’un nœud. En utilisant une instance par nœud, vous pouvez également vous assurer que les secrets ne seront jamais stockés sur le même nœud, ce qui renforce la sécurité globale de votre cluster. En suivant les étapes décrites dans cet article, vous pouvez être sûr de déployer un cluster Vault haute disponibilité solide et fiable sur votre cluster Kubernetes.

Plusieurs points n’ont pas été approfondis dans cet article comme la configuration d’un LDAP, l’utilisation de l’authentification Kubernetes ou encore la sauvegarde. Toutefois en parcourant le dépôt de code vous trouverez des éléments de configuration pour le LDAP, l’authentification Kubernetes et une sauvegarde manuelle via un cronjob (ou des liens dans la bibliographie pour utiliser Velero pour des solutions de production).

Bibliographie

Operator Banzaicloud

Bank-Vaults Bank-Vaults Bank-Vaults provides various tools for Hashicorp Vault to make its use easier: A wrapper for the official…banzaicloud.com

GitHub — banzaicloud/bank-vaults: A Vault swiss-army knife: a K8s operator, Go client with… A Vault swiss-army knife: a K8s operator, Go client with automatic token renewal, automatic configuration, multiple…github.com

Documentation Hashicorp

AppRole Pull Authentication | Vault | HashiCorp Developer Before a client can interact with Vault, it must authenticate against an auth method to acquire a token. This token has…developer.hashicorp.com

Audit Device Logs and Incident Response with Elasticsearch | Vault | HashiCorp Developer As a Vault operator or security practitioner, you need to respond to common incidents which can arise in the operation…developer.hashicorp.com

Backuper son cluster avec Velero

Velero Introductionfgtech.medium.com

bank-vaults/examples/backup at main · banzaicloud/bank-vaults A Vault swiss-army knife: a K8s operator, Go client with automatic token renewal, automatic configuration, multiple…github.com