From 91a0f09b491371cd4bc0e53267beefd255153a14 Mon Sep 17 00:00:00 2001 From: Gabriel Radureau Date: Sun, 26 Jul 2026 09:26:05 +0200 Subject: [PATCH] =?UTF-8?q?refactor(vault)=20=E2=80=94=20lire=20ses=20iden?= =?UTF-8?q?tifiants=20MinIO=20devient=20une=20propri=C3=A9t=C3=A9=20de=20l?= =?UTF-8?q?a=20plateforme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Retour fondateur : « je pensais que tools#21 contribuerait à app_policy pour une policy kvv2/minio/ ». Il a raison, et mon choix initial était le plus faible des deux. Mon objection — ne donner le droit qu'aux apps qui en ont besoin — ne tient pas à l'examen : la règle porte le NOM de l'app, donc elle ne peut jamais exposer que ses propres clés. Il n'y a aucun privilège à préserver. Une app qui ne stocke rien lit un chemin qui n'existe pas : une règle inerte, pas un droit. Son argument, lui, porte : savoir lire ses propres identifiants de stockage est une propriété de la PLATEFORME, pas une exception par application. Et `kv_read_paths` est documenté comme la trappe pour un secret appartenant à une AUTRE app (les creds GCS de Longhorn pour l'ERP) — y ranger un motif standard l'aurait rendu invisible et aurait obligé à le redéclarer à chaque app. La règle passe donc dans `app_policy`, en prod ET pour chaque instance non-prod (symétrie stricte), sur deux chemins : le document `kvv2/data/minio/` et ses descendants. Un cran plus loin que la demande : la règle est INCONDITIONNELLE, sans drapeau. Conséquence — déclarer un consommateur MinIO se fait désormais à UN SEUL endroit, `var.consumers` du pipeline minio. Aucune synchronisation à tenir entre deux fichiers, donc rien à oublier. Le `kv_read_paths` que j'avais ajouté à kadans est retiré : il faisait double emploi. tofu fmt propre · tofu validate réussi sur hashicorp-vault/iac. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01CoafGWmRVESaWX819USUUA --- hashicorp-vault/iac/factory_auth.tf | 2 +- .../iac/modules/app_policy/main.tf | 34 +++++++++++++++++++ hashicorp-vault/iac/terraform.tfvars | 10 +----- minio/README.md | 18 ++++++---- 4 files changed, 48 insertions(+), 16 deletions(-) diff --git a/hashicorp-vault/iac/factory_auth.tf b/hashicorp-vault/iac/factory_auth.tf index d9a1fc0..ba87ce1 100644 --- a/hashicorp-vault/iac/factory_auth.tf +++ b/hashicorp-vault/iac/factory_auth.tf @@ -1,5 +1,5 @@ locals { - factory_crowdsec_conf_sa_name = "factory-ansible-tool-crowdsec-traefik-plugin" + factory_crowdsec_conf_sa_name = "factory-ansible-tool-crowdsec-traefik-plugin" } diff --git a/hashicorp-vault/iac/modules/app_policy/main.tf b/hashicorp-vault/iac/modules/app_policy/main.tf index 6e7a34d..f93c0fb 100644 --- a/hashicorp-vault/iac/modules/app_policy/main.tf +++ b/hashicorp-vault/iac/modules/app_policy/main.tf @@ -178,6 +178,30 @@ data "vault_policy_document" "app" { path = "postgres/creds/${local.name}*" capabilities = ["read"] } + # Identifiants de SON compte de service MinIO, provisionné par le pipeline + # `minio` (tools/minio/iac/consumers.tf) dans l'espace Vault de MinIO. + # + # INCONDITIONNEL, et c'est voulu : le chemin porte le nom de l'app, donc cette + # règle ne peut jamais exposer que ses PROPRES clés. Une app qui ne stocke rien + # lit un chemin qui n'existe pas — une règle inerte, pas un privilège. + # + # Pourquoi ici plutôt que dans `kv_read_paths` de chaque app : savoir lire ses + # propres identifiants de stockage est une propriété de la PLATEFORME, pas une + # exception par application. `kv_read_paths` est la trappe pour un secret + # appartenant à une AUTRE app (les creds GCS de Longhorn pour l'ERP) ; y ranger + # un motif standard le rendrait invisible et obligerait à le redéclarer partout. + # Conséquence pratique : déclarer un consommateur MinIO se fait à UN seul + # endroit — `var.consumers` du pipeline minio. Aucune synchronisation à tenir. + rule { + path = "kvv2/data/minio/${local.name}/*" + capabilities = ["read", "list"] + } + rule { + # Le secret est écrit à `kvv2/minio/` (sans sous-chemin) : la règle + # ci-dessus couvre les descendants, celle-ci le document lui-même. + path = "kvv2/data/minio/${local.name}" + capabilities = ["read", "list"] + } # Extra shared paths this app's prod runtime may read (e.g. backup creds). dynamic "rule" { for_each = var.kv_read_paths @@ -204,6 +228,16 @@ data "vault_policy_document" "app_non_prod" { path = "postgres/creds/${each.key}*" capabilities = ["read"] } + # Même règle qu'en prod (voir le commentaire de vault_policy_document.app) : + # chaque instance lit les identifiants MinIO portant SON nom. + rule { + path = "kvv2/data/minio/${each.key}/*" + capabilities = ["read", "list"] + } + rule { + path = "kvv2/data/minio/${each.key}" + capabilities = ["read", "list"] + } } resource "vault_policy" "app_non_prod" { for_each = toset(local.non_prod_instances) diff --git a/hashicorp-vault/iac/terraform.tfvars b/hashicorp-vault/iac/terraform.tfvars index 14cdff4..b0921e7 100644 --- a/hashicorp-vault/iac/terraform.tfvars +++ b/hashicorp-vault/iac/terraform.tfvars @@ -24,13 +24,5 @@ applications = [ service_account_namespaces = ["tools"] }, { name = "prospection" }, - { - name = "kadans" - # Le pod lit les clés de SON compte de service MinIO, écrites par le pipeline - # `minio` dans son propre espace (minio/iac/consumers.tf). Le mécanisme est - # celui qui existait déjà pour l'ERP et les creds GCS de Longhorn — aucun - # module central n'a eu à changer. Le root de MinIO, lui, ne sort jamais du - # pipeline minio : l'app ne reçoit qu'une clé bornée à son bucket. - kv_read_paths = ["kvv2/data/minio/kadans"] - }, + { name = "kadans" }, ] diff --git a/minio/README.md b/minio/README.md index 2e8276d..8e757d4 100644 --- a/minio/README.md +++ b/minio/README.md @@ -106,16 +106,22 @@ demande aucune modification des modules Vault centraux. ``` Le plan crée une politique MinIO **bornée à ce bucket**, un compte de service, et écrit ses clés dans `kvv2/minio/mon-app`. -3. **La lecture** — dans `hashicorp-vault/iac/terraform.tfvars`, l'app déclare : - ```hcl - { name = "mon-app", kv_read_paths = ["kvv2/data/minio/mon-app"] } - ``` - `kv_read_paths` **existait déjà** (l'ERP s'en sert pour les creds GCS de - Longhorn) : rien à changer dans `app_policy` ni `app_roles`. +3. **La lecture** — *rien à faire.* Le module central `app_policy` accorde à + **toute** app la lecture de `kvv2/data/minio/`. La règle est + inconditionnelle et c'est voulu : le chemin porte le nom de l'app, donc elle + ne peut jamais exposer que ses propres clés ; une app qui ne stocke rien lit + un chemin qui n'existe pas. Puis, côté app : une `VaultStaticSecret` sur `kvv2/minio/` et l'injection des variables dans le Deployment. +> **Un seul endroit déclare un consommateur** : `var.consumers`, ici. Rien à +> synchroniser dans le tfvars central, donc rien à oublier. C'est la raison pour +> laquelle la règle vit dans le module plutôt que dans `kv_read_paths` — cette +> dernière est la trappe pour lire un secret appartenant à une AUTRE app (les +> creds GCS de Longhorn pour l'ERP) ; y ranger un motif standard le rendrait +> invisible et obligerait à le redéclarer à chaque fois. + ### Pourquoi les clés vivent ICI et pas chez l'app Seul ce pipeline possède les identifiants **root** de MinIO. Si chaque app créait