docs(minio) — le raisonnement part dans l'ADR, le code garde ce qui protège
Helm Charts / Detect changed charts (push) Successful in 22s
Helm Charts / Detect changed charts (pull_request) Successful in 16s
MinIO / Auth with gitea for vault (push) Failing after 8m33s
MinIO / Tofu - minio IAC (push) Has been skipped
MinIO / Auth with gitea for vault (pull_request) Failing after 8m32s
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 (push) Has been skipped
Helm Charts / Library charts tool (pull_request) Has been skipped
Hashicorp Vault / Auth with gitea for vault (push) Failing after 8m31s
Hashicorp Vault / Tofu - Vault IAC (push) Has been skipped
Helm Charts / Application charts pgcat (push) Has been skipped
Helm Charts / Application charts pgcat (pull_request) Has been skipped
Helm Charts / Detect changed charts (push) Successful in 22s
Helm Charts / Detect changed charts (pull_request) Successful in 16s
MinIO / Auth with gitea for vault (push) Failing after 8m33s
MinIO / Tofu - minio IAC (push) Has been skipped
MinIO / Auth with gitea for vault (pull_request) Failing after 8m32s
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 (push) Has been skipped
Helm Charts / Library charts tool (pull_request) Has been skipped
Hashicorp Vault / Auth with gitea for vault (push) Failing after 8m31s
Hashicorp Vault / Tofu - Vault IAC (push) Has been skipped
Helm Charts / Application charts pgcat (push) Has been skipped
Helm Charts / Application charts pgcat (pull_request) Has been skipped
Les décisions de conception (qui déclare, qui détient, pourquoi les octets ne passent pas par l'API) vivent désormais dans `factory/doc/adr/20260726-stockage-objet-minio.md`. Le code ne garde que ce qu'un relecteur ne peut pas deviner et qui l'empêcherait de casser quelque chose : l'absence VOLONTAIRE de s3:GetObject/PutObject dans la politique du provisionneur, l'absence VOLONTAIRE de basic-auth sur l'ingress S3, le caractère inconditionnel de la règle Vault, et l'avertissement sur les noms d'actions MinIO non éprouvés. Chaque fichier pointe l'ADR. 283 → 230 lignes sur minio/iac. tofu fmt propre, tofu validate réussi sur les deux racines. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01CoafGWmRVESaWX819USUUA
This commit is contained in:
@@ -1,38 +1,13 @@
|
||||
# ── Module `minio_app` — une app déclare SES buckets, depuis SON dépôt ───────
|
||||
#
|
||||
# Chacun son périmètre (fondateur 2026-07-26) : les buckets d'une application
|
||||
# appartiennent au dépôt de cette application, pas à l'infra partagée. `tools`
|
||||
# ne fait que STANDARDISER la déclaration — il ne la détient pas. Sans ça,
|
||||
# chaque bucket de chaque app deviendrait une PR sur le dépôt commun.
|
||||
#
|
||||
# Usage, depuis l'`iac/` de l'app :
|
||||
#
|
||||
# module "stockage" {
|
||||
# source = "git::…/tools.git//minio/iac/modules/minio_app?depth=1&ref=main"
|
||||
# app = "kadans"
|
||||
# buckets = ["kadans-videos"]
|
||||
# providers = { minio = minio }
|
||||
# }
|
||||
#
|
||||
# Ce que le module garantit :
|
||||
# · les buckets sont PRIVÉS — l'accès passe par des URL présignées ;
|
||||
# · le compte de service ne peut RIEN toucher d'autre que ces buckets-là ;
|
||||
# · ses clés atterrissent dans `kvv2/minio/<app>`, que le module Vault central
|
||||
# autorise déjà l'app à lire (règle inconditionnelle : le chemin porte le nom
|
||||
# de l'app, donc il ne peut exposer que ses propres clés).
|
||||
# Module `minio_app` — une app déclare SES buckets depuis SON dépôt.
|
||||
# Décisions : factory/doc/adr/20260726-stockage-objet-minio.md
|
||||
|
||||
resource "minio_s3_bucket" "app" {
|
||||
for_each = toset(var.buckets)
|
||||
bucket = each.key
|
||||
acl = "private"
|
||||
# Les objets sont DÉRIVÉS (rendus de transfert) et régénérables depuis les
|
||||
# originaux restés sur l'appareil : un `force_destroy` accidentel ne détruit
|
||||
# donc rien d'irremplaçable, mais on l'interdit quand même — la destruction
|
||||
# d'un bucket doit être un geste explicite, pas un effet de bord d'un plan.
|
||||
force_destroy = false
|
||||
for_each = toset(var.buckets)
|
||||
bucket = each.key
|
||||
acl = "private" # l'accès passe par des URL présignées
|
||||
force_destroy = false # détruire un bucket doit être un geste explicite
|
||||
}
|
||||
|
||||
# La politique : SES buckets, rien d'autre. Ni les autres, ni l'administration.
|
||||
resource "minio_iam_policy" "app" {
|
||||
name = "${var.app}-app"
|
||||
policy = jsonencode({
|
||||
@@ -44,8 +19,6 @@ resource "minio_iam_policy" "app" {
|
||||
Resource = [for b in var.buckets : "arn:aws:s3:::${b}/*"]
|
||||
},
|
||||
{
|
||||
# Nécessaire pour qu'un client S3 vérifie l'existence d'un bucket et
|
||||
# liste SES objets — jamais ceux d'une autre app.
|
||||
Effect = "Allow"
|
||||
Action = ["s3:ListBucket", "s3:GetBucketLocation"]
|
||||
Resource = [for b in var.buckets : "arn:aws:s3:::${b}"]
|
||||
@@ -60,12 +33,9 @@ resource "random_password" "app" {
|
||||
}
|
||||
|
||||
resource "minio_iam_user" "app" {
|
||||
name = "${var.app}-app"
|
||||
# Le mot de passe EST la clé secrète S3 : généré ici, jamais choisi.
|
||||
secret = random_password.app.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
|
||||
name = "${var.app}-app"
|
||||
secret = random_password.app.result
|
||||
force_destroy = false # une rotation ne recrée pas l'utilisateur : les objets gardent leur propriétaire
|
||||
}
|
||||
|
||||
resource "minio_iam_user_policy_attachment" "app" {
|
||||
@@ -73,9 +43,7 @@ resource "minio_iam_user_policy_attachment" "app" {
|
||||
policy_name = minio_iam_policy.app.id
|
||||
}
|
||||
|
||||
# Les clés, dans l'espace Vault de MinIO — lu par le pod de l'app.
|
||||
# `MINIO_BUCKETS` les liste toutes pour que l'app n'ait pas à les redéclarer ;
|
||||
# `MINIO_BUCKET` reste le premier, pour celles qui n'en ont qu'un.
|
||||
# Lu par le pod de l'app — `app_policy` lui accorde déjà ce chemin.
|
||||
resource "vault_kv_secret_v2" "app" {
|
||||
mount = "kvv2"
|
||||
name = "minio/${var.app}"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
output "vault_path" {
|
||||
value = "kvv2/minio/${var.app}"
|
||||
description = "Où le pod lira ses identifiants (VaultStaticSecret). Le module Vault central autorise déjà l'app à lire ce chemin."
|
||||
description = "Où le pod lira ses identifiants (VaultStaticSecret)."
|
||||
}
|
||||
|
||||
output "buckets" {
|
||||
value = var.buckets
|
||||
description = "Écho des buckets créés — utile pour poser la valeur `bucket` du chart sans la réécrire."
|
||||
description = "Écho des buckets créés."
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
variable "app" {
|
||||
type = string
|
||||
description = "Nom de l'app — celui de son rôle Vault. Décide du chemin du secret (kvv2/minio/<app>) et du nom du compte de service."
|
||||
description = "Nom de l'app (= son rôle Vault). Décide du chemin du secret et du nom du compte de service."
|
||||
}
|
||||
|
||||
variable "buckets" {
|
||||
type = list(string)
|
||||
description = "Les buckets de cette app. Ils sont CRÉÉS ici (privés) et le compte de service n'a de droits que sur eux. Plusieurs sont possibles — deux contenus aux cycles de vie différents méritent deux buckets."
|
||||
description = "Ses buckets, créés ici (privés). Le compte de service n'a de droits que sur eux."
|
||||
}
|
||||
|
||||
variable "endpoint" {
|
||||
type = string
|
||||
default = "s3.arcodange.fr"
|
||||
description = "Hôte de l'API S3, SANS schéma. Public : le runner CI n'est pas dans le LAN et `.lab` ne s'y résout pas."
|
||||
description = "Hôte de l'API S3, sans schéma. Public : le runner CI n'est pas dans le LAN."
|
||||
}
|
||||
|
||||
@@ -23,9 +23,7 @@ provider "vault" {
|
||||
}
|
||||
}
|
||||
|
||||
# Provider MinIO — pour créer les COMPTES DE SERVICE des apps consommatrices.
|
||||
# Il parle à l'API S3 publique (s3.arcodange.fr, exposée par le chart) : le
|
||||
# runner CI n'est pas dans le LAN, et `.lab` ne s'y résout pas.
|
||||
# Provider MinIO — crée le compte de provisionnement (provisioner.tf).
|
||||
provider "minio" {
|
||||
minio_server = var.minio_endpoint
|
||||
minio_user = local.config.rootUser
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
# ── Le compte PROVISIONNEUR : ce que `tools` doit fournir en plus du module ───
|
||||
# Compte de PROVISIONNEMENT : crée buckets, politiques et comptes de service —
|
||||
# aucun droit sur les objets. C'est lui que lisent les rôles CI des apps, pour
|
||||
# qu'elles déclarent leurs buckets sans qu'on leur confie le root.
|
||||
# Décisions : factory/doc/adr/20260726-stockage-objet-minio.md
|
||||
#
|
||||
# Une app qui déclare ses buckets depuis son propre dépôt doit pouvoir les
|
||||
# créer — donc disposer de droits d'ADMINISTRATION sur MinIO. Lui donner le
|
||||
# ROOT serait absurde : le root lit et écrit TOUS les objets de TOUTES les apps.
|
||||
#
|
||||
# Ce compte-ci ne peut que PROVISIONNER : créer un bucket, une politique, un
|
||||
# compte de service, et les attacher. Il n'a AUCUN droit de lecture ou
|
||||
# d'écriture sur les objets. Une app compromise pourrait créer des buckets —
|
||||
# une nuisance —, pas lire les vidéos d'une autre.
|
||||
#
|
||||
# Le root, lui, reste dans `kvv2/minio/config`, que seul le rôle CI `minio`
|
||||
# peut lire.
|
||||
#
|
||||
# ⚠ NON VÉRIFIÉ CONTRE LE SERVEUR : les noms d'actions d'administration MinIO
|
||||
# ci-dessous viennent de la documentation, pas d'un essai — je n'ai pas
|
||||
# d'identifiants admin en main. Le PREMIER `apply` les confirmera ou les
|
||||
# corrigera ; c'est le seul point de cette PR que je ne peux pas prouver ici.
|
||||
# ⚠ Noms d'actions issus de la documentation MinIO, NON éprouvés contre le
|
||||
# serveur : le premier apply les confirmera ou les corrigera.
|
||||
resource "minio_iam_policy" "provisioner" {
|
||||
name = "provisioner"
|
||||
policy = jsonencode({
|
||||
@@ -37,8 +26,7 @@ resource "minio_iam_policy" "provisioner" {
|
||||
Resource = ["arn:aws:s3:::*"]
|
||||
},
|
||||
{
|
||||
# Créer et inspecter un bucket — mais PAS lire ni écrire ses objets :
|
||||
# `s3:GetObject` et `s3:PutObject` sont volontairement absents.
|
||||
# s3:GetObject / s3:PutObject volontairement ABSENTS.
|
||||
Effect = "Allow"
|
||||
Action = ["s3:CreateBucket", "s3:DeleteBucket", "s3:ListAllMyBuckets", "s3:GetBucketLocation", "s3:GetBucketPolicy", "s3:PutBucketPolicy"]
|
||||
Resource = ["arn:aws:s3:::*"]
|
||||
@@ -63,9 +51,6 @@ resource "minio_iam_user_policy_attachment" "provisioner" {
|
||||
policy_name = minio_iam_policy.provisioner.id
|
||||
}
|
||||
|
||||
# Lisible par le rôle CI de CHAQUE app (règle générique ajoutée à `app_policy`) :
|
||||
# c'est ce qui permet à une app de provisionner ses propres buckets sans qu'on
|
||||
# lui confie le root.
|
||||
resource "vault_kv_secret_v2" "provisioner" {
|
||||
mount = "kvv2"
|
||||
name = "minio/provisioner"
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
variable "minio_endpoint" {
|
||||
type = string
|
||||
default = "s3.arcodange.fr"
|
||||
description = "Hôte de l'API S3, SANS schéma (le provider ajoute https via minio_ssl). Public : le runner CI n'est pas dans le LAN et `.lab` ne s'y résout pas."
|
||||
description = "Hôte de l'API S3, sans schéma. Public : le runner CI n'est pas dans le LAN."
|
||||
}
|
||||
|
||||
# `consumers` a été SUPPRIMÉE (fondateur 2026-07-26) : la liste se lit dans
|
||||
# `values.yaml` du chart — créer un bucket EST la déclaration. Une liste de plus
|
||||
# aurait été une liste à tenir synchronisée, donc une liste à oublier.
|
||||
|
||||
Reference in New Issue
Block a user