Vault Secrets Operator est disponible depuis quelques mois. Il s'agit de l'opérateur Kubernetes officiel pour la synchronisation des secrets entre Vault et Kubernetes.
Dans cet article, nous allons voir comment installer et configurer Vault Secrets Operator sur Kubernetes et plus précisément: comment configurer l'authentification sur Vault, le déploiement de l'opérateur VSO, ainsi que les CRDs associés pour synchroniser des secrets Kubernetes. Les clusters GKE et Vault sont déjà déployés, nous allons utiliser FluxCD pour le déploiement des manifests Kubernetes et faire cela as code, avec l'aide de Terraform.
Créer un secret dans Vault
Nous allons commencer par créer un Secret Engine de type KVv2 (Key-Value v2)...
resource "vault_mount" "kvv2" {
path = "secret-kv"
type = "kv"
options = { version = "2" }
description = "KV Version 2"
}
...et un secret dans Vault que l'on synchronisera dans Kubernetes.
resource "vault_kv_secret_v2" "secret" {
mount = vault_mount.kvv2.path
name = "my-application/bdd-access"
data_json = toJson({user="username", password="secret-pass"})
delete_all_versions = true
}
Configurer l'authentification Vault
Pour que Kubernetes puisse s'authentifier sur Vault, il y a 2 méthodes (nommé AuthMethod dans le vocabulaire Vault) : via l'AuthMethod kubernetes ou via l'AuthMethod JWT/OIDC.
L'AuthMethod JWT présente plusieurs avantages : ne pas nécessiter de connexion depuis Vault vers l'api-server du cluster Kubernetes (TokenReview) et ne pas nécessiter de conserver de credentials côté Vault.
Nous allons donc utiliser cette méthode d'authentification.
1er étape: récupérer la configuration OpenID du cluster GKE
Nous avons besoin des certificats publics du service OpenID Connect du cluster GKE. Ces informations sont disponibles sur un endpoint de Google Cloud. Mais il n'y a actuellement pas de ressource Terraform dans le provider Google qui permette de récupérer ces informations. Il y a d'ailleurs une issue ouverte à ce sujet.
Solution : nous avons développé un provider custom pour récupérer ces informations, qui va lire le certificat (au format JWKS) et le transformer dans le format attendu par Vault (PEM).
Vous pouvez trouver ce provider sur la registry publique sous le nom : alex-ikse/wellknownoidc
data "wellknownoidc_document" "k8s" {
discovery_url = "https://container.googleapis.com/v1/projects/${var.gcp_project_id}/locations/${var.gke_region}/clusters/${var.gke_cluster_name}"
}
data "wellknownoidc_jwks" "k8s" {
jwks_uri = data.wellknownoidc_document.k8s.jwks_uri
}
Configurer la connexion dans Vault
Avec ces informations, nous allons donc créer une authentification de type JWT sur Vault, en reprenant les certificats publics de notre service OpenID Connect.
resource "vault_jwt_auth_backend" "kubernetes_jwt" {
path = "jwt/kubernetes/gke-cluster"
tune {
default_lease_ttl = "1m"
max_lease_ttl = "1m"
token_type = "batch"
}
jwt_validation_pubkeys = data.wellknownopenidconfiguration_jwks.k8s.pem
}
Nous allons créer une policy qui va indiquer quels sont les droits sur Vault pour notre service account kubernetes. Ici, des droits en lecture seule (read
), dans le secret engine (de type key-value) secret-kv
sous le chemin my-application
et tous les secrets en dessous.
resource "vault_policy" "app_policy" {
name = "my-application-policy"
policy = <<EOT
path "secret-kv/data/my-application/*" {
capabilities = ["read"]
}
EOT
}
Nous allons finalement créer un rôle qui va faire le liant entre l'authentification JWT que nous avons créé précédemment, la policy en read-only, l'audience que nous allons spécifier avec l'authentification et le subject qui sera spécifier par le service OpenID Connect de GKE lors de la génération du JWT.
resource "vault_jwt_auth_backend_role" "role" {
backend = vault_jwt_auth_backend.kubernetes_jwt.path
role_name = "my-application/vault-secrets-syncer"
token_policies = ["my-application-policy"]
# TODO changer perimeter
bound_audiences = [var.perimeter]
bound_subject = "system:serviceaccount:my-application:vault-secrets-syncer"
user_claim = "sub"
role_type = "jwt"
}
Déployer l'opérateur Vault Secrets Operator dans GKE
Vault Secrets Operator est disponible depuis quelques mois, la dernière version étant la v0.7.1. La documentation est disponible ici: developer.hashicorp.com/vault/docs/platform/k8s/vso.
Pour FluxCD, nous allons référencer le repository Helm de Hashicorp :
---
apiVersion: source.toolkit.fluxcd.io/v1beta2
kind: HelmRepository
metadata:
name: vso
namespace: vault-system
spec:
interval: 60m
url: https://helm.releases.hashicorp.com
Puis nous allons déployer l'opérateur :
---
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: vso
spec:
releaseName: vso
targetNamespace: vault-system
timeout: 10m
interval: 10m
chart:
spec:
chart: vault-secrets-operator
version: 0.4.3
sourceRef:
kind: HelmRepository
name: vso
namespace: vault-system
interval: 1m
values:
fullnameOverride: vso
defaultVaultConnection:
enabled: true
address: https://vault.mycompany.com/
Configurer l'authentification sur Vault depuis Kubernetes
Une fois notre opérateur déployé, nous allons configurer comment Kubernetes va concrètement s'authentifier sur Vault.
Une fois authentifié sur Vault, il est nécessaire d'avoir un rôle pour y associer des policies, donc nous allons d'abord créer un rôle sur Vault :
Il nous faut d'abord un service account Kubernetes, qui va être l'identité utilisée pour se connecter à Vault.
apiVersion: v1
kind: ServiceAccount
metadata:
name: vault-secrets-syncer
namespace: my-application
Que nous allons utiliser dans la resource VaultAuth
qui va indiquer à l'opérateur Vault comment s'authentifier au serveur Vault.
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
name: my-application-vault-auth
namespace: my-application
spec:
method: jwt
mount: jwt/kubernetes/gke-cluster
namespace: my-application
jwt:
role: my-application/vault-secrets-syncer
serviceAccount: vault-secrets-syncer
audiences:
#TODO corrigé
- {{ gitops_repo.perimeter }}
tokenExpirationSeconds: 600
Et finalement, nous allons créer la resource VaultSecrets
qui va faire concrètement la synchronisation du secret entre Vault et GKE.
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: user-pg
namespace: my-application
spec:
type: kv-v2
refreshAfter: 30s
# Let to true to detect drift detection
hmacSecretData: true
# The name of the authentication used to connect to vault,
vaultAuthRef: my-application-vault-auth
mount: secret-kv
path: my-application/bdd-access
# The destination of the secret
destination:
name: my-secret-bdd-user
create: true
# It's possible to restart your application automatically once the secret has roll out.
rolloutRestartTargets:
- kind: Deployment
name: my-application-deployment
Et voilà un schéma qui résume toute la procédure
Cet article explique comment installer et configurer Vault Secrets Operator sur un cluster GKE en utilisant Terraform et FluxCD. Il couvre la configuration de l'authentification via JWT, le déploiement de l'opérateur VSO, et la synchronisation des secrets entre Vault et Kubernetes. Grâce à cette méthode, vous pouvez gérer vos secrets de manière sécurisée et automatisée, assurant ainsi une meilleure sécurité et efficacité des déploiements sur Kubernetes.