Aller au contenu

Comment installer et configurer Vault Secrets Operator sur GKE avec Terraform et FluxCD

Cet article explique comment installer et configurer Vault Secrets Operator sur un cluster GKE en utilisant Terraform et FluxCD.

Stockage de secrets dans Vault déployé avec Terraform et FluxCD

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

Schéma résumant la procédure à suivre

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.

Dernier