Helm Charts / Detect changed charts (push) Successful in 14s
Helm Charts / Detect changed charts (pull_request) Successful in 14s
Helm Charts / Application charts pgcat (push) Has been cancelled
Helm Charts / Library charts tool (push) Has been cancelled
MinIO / Tofu - minio IAC (push) Has been cancelled
MinIO / Auth with gitea for vault (push) Has been cancelled
MinIO / Auth with gitea for vault (pull_request) Failing after 8m31s
MinIO / Tofu - minio IAC (pull_request) Has been skipped
Hashicorp Vault / Auth with gitea for vault (pull_request) Failing after 8m32s
Hashicorp Vault / Tofu - Vault IAC (pull_request) Has been skipped
Helm Charts / Library charts tool (pull_request) Has been skipped
Helm Charts / Application charts pgcat (pull_request) Has been skipped
« Je ne vois pas le mal à donner la permission de lire sur un chemin qui n'existe pas. Je préfère ne pas m'embêter avec consumers ou autre. » (fondateur, 26/07) `var.consumers` disparaît. Le plan LIT `values.yaml` du chart — le même fichier qu'Helm consomme — et provisionne un compte de service par bucket. Créer un bucket EST la déclaration : il devient impossible d'avoir un bucket sans son compte, ou un compte sans son bucket. Une liste de plus aurait été une liste à tenir synchronisée, donc une liste à oublier. La convention qui rend ça possible : UN BUCKET PAR APP, NOMMÉ COMME ELLE. Le bucket passe donc de `kadans-videos` à `kadans`. Il est VIDE aujourd'hui — le renommer maintenant ne coûte rien ; dans un mois ce serait une migration. Tout en découle sans être écrit ailleurs : le compte `<app>-app` borné à ce seul bucket, le secret `kvv2/minio/<app>`, et la lecture que `app_policy` accorde déjà à toute app sur `kvv2/data/minio/<son nom>`. Vérifié plutôt que supposé : `yamldecode` lit bien ce values.yaml, ancres YAML comprises (testé en isolation avant d'écrire le plan). tofu fmt propre, tofu validate réussi. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01CoafGWmRVESaWX819USUUA
88 lines
3.5 KiB
Terraform
88 lines
3.5 KiB
Terraform
# ── Un compte de service par BUCKET, sans rien déclarer de plus ──────────────
|
|
#
|
|
# Il n'y a PAS de liste de consommateurs : elle se lit dans `values.yaml` du
|
|
# chart. Créer un bucket EST la déclaration (fondateur 2026-07-26 : « je préfère
|
|
# ne pas m'embêter avec consumers ou autre »). Rien à synchroniser entre deux
|
|
# fichiers, donc rien à oublier — et aucune divergence possible.
|
|
#
|
|
# La convention qui rend ça possible : UN BUCKET PAR APP, NOMMÉ COMME ELLE.
|
|
# Tout en découle sans être écrit nulle part ailleurs :
|
|
# · le compte de service `<app>-app`, borné à ce seul bucket ;
|
|
# · le secret `kvv2/minio/<app>` ;
|
|
# · la lecture, que le module central `app_policy` accorde déjà à TOUTE app
|
|
# sur `kvv2/data/minio/<son nom>`, inconditionnellement — une app qui ne
|
|
# stocke rien y lit un chemin qui n'existe pas, ce qui n'est pas un droit.
|
|
#
|
|
# 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 son propre compte de service,
|
|
# il faudrait donner ce root à chaque rôle CI — c'est-à-dire à tout le monde.
|
|
# Ici il ne sort jamais, et l'app ne reçoit qu'une clé qui ne peut rien lire
|
|
# d'autre que son bucket. Un compte qui fuite ne donne accès qu'aux objets qu'il
|
|
# gérait déjà.
|
|
|
|
locals {
|
|
# Le MÊME fichier que celui qu'Helm consomme : impossible de créer un bucket
|
|
# sans son compte de service, ou un compte sans son bucket.
|
|
buckets = [for b in yamldecode(file("${path.module}/../values.yaml")).minio.buckets : b.name]
|
|
}
|
|
|
|
# La politique d'accès : SON bucket, rien d'autre. Ni listing des autres
|
|
# buckets, ni administration.
|
|
resource "minio_iam_policy" "app" {
|
|
for_each = toset(local.buckets)
|
|
name = "${each.key}-app"
|
|
policy = jsonencode({
|
|
Version = "2012-10-17"
|
|
Statement = [
|
|
{
|
|
Effect = "Allow"
|
|
Action = ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"]
|
|
Resource = ["arn:aws:s3:::${each.key}/*"]
|
|
},
|
|
{
|
|
# Nécessaire pour qu'un client S3 vérifie l'existence du bucket et liste
|
|
# SES objets — jamais ceux d'un autre.
|
|
Effect = "Allow"
|
|
Action = ["s3:ListBucket", "s3:GetBucketLocation"]
|
|
Resource = ["arn:aws:s3:::${each.key}"]
|
|
},
|
|
]
|
|
})
|
|
}
|
|
|
|
resource "random_password" "app" {
|
|
for_each = toset(local.buckets)
|
|
length = 40
|
|
special = false # les outils S3 transportent mal certains caractères en URL
|
|
}
|
|
|
|
resource "minio_iam_user" "app" {
|
|
for_each = toset(local.buckets)
|
|
name = "${each.key}-app"
|
|
# Le mot de passe EST la clé secrète S3 : généré ici, jamais choisi.
|
|
secret = random_password.app[each.key].result
|
|
# `false` : une rotation du mot de passe ne recrée pas l'utilisateur, donc les
|
|
# objets déjà déposés gardent leur propriétaire.
|
|
force_destroy = false
|
|
}
|
|
|
|
resource "minio_iam_user_policy_attachment" "app" {
|
|
for_each = toset(local.buckets)
|
|
user_name = minio_iam_user.app[each.key].id
|
|
policy_name = minio_iam_policy.app[each.key].id
|
|
}
|
|
|
|
# Les clés, dans l'espace Vault de MINIO. L'app y accède par la règle que
|
|
# `app_policy` accorde à toutes : `kvv2/data/minio/<son nom>`.
|
|
resource "vault_kv_secret_v2" "app" {
|
|
for_each = toset(local.buckets)
|
|
mount = "kvv2"
|
|
name = "minio/${each.key}"
|
|
data_json = jsonencode({
|
|
MINIO_ENDPOINT = var.minio_endpoint
|
|
MINIO_BUCKET = each.key
|
|
MINIO_ACCESS_KEY = minio_iam_user.app[each.key].id
|
|
MINIO_SECRET_KEY = random_password.app[each.key].result
|
|
})
|
|
}
|