Files
factory/doc/runbooks/new-web-app/03-vault-platform.md
Gabriel Radureau 8330d82225 docs(runbooks): add "new web app" setup runbook under doc/runbooks/
Document, as a tree-docs tree, the end-to-end procedure to stand up a new
web application on the Arcodange platform — a mechanic spread across the
factory, tools and app repos with non-trivial ordering dependencies.

Covers: Gitea repo creation (org-secret inheritance), Postgres DB + owner
role (factory/postgres/iac), platform Vault declaration (gitea_cicd_<app>
+ policies, tools/hashicorp-vault/iac), the app Helm chart (VSO dynamic
secrets via pgbouncer), the app Terraform (app_roles module), the CI
workflows (tofu apply + image build, incl. the copy-pasted role pitfall),
and ArgoCD registration (factory/argocd/values.yaml). Adds a naming-
conventions concept page and an ordered checklist.

Wires the legacy doc/adr "setup hello world web app" item and the factory
README to the runbook. New docs live under doc/ (singular) per the PR #8
convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 17:22:30 +02:00

5.5 KiB

Factory > Doc > Runbooks > Nouvelle application web > 3. Vault plateforme

3. Déclarer l'app côté Vault plateforme

Status: Active Upstream: 1. Dépôt Gitea Downstream: 5. Terraform de l'app, 6. Workflows CI Related: 2. Base de données · 4. Chart Helm · Conventions de nommage


Summary

Avant que la CI de l'app puisse gérer ses propres secrets, Vault doit connaître l'app : il lui faut un rôle JWT de CI (gitea_cicd_<app>) pour que la pipeline s'authentifie, une policy CI (<app>-ops) qui l'autorise à créer ses rôles Postgres/K8s, et une policy runtime (<app>) que le pod utilisera. Tout ça est généré par un module, depuis une seule ligne ajoutée côté plateforme dans le dépôt tools.

Action

Ajouter une entrée pour l'app au set applications de tools/hashicorp-vault/iac/terraform.tfvars :

applications = [
  { name = "webapp" },
  { name = "erp" },
  { name = "<app>" },   # ← ajouter
  # options possibles :
  # {
  #   name                       = "<app>"
  #   ops_policies               = ["factory__cf_r2_arcodange_tf"]  # policies ops supplémentaires (ex. token Cloudflare)
  #   service_account_names      = ["cloudflared"]                  # SA additionnels autorisés à prendre la policy runtime
  #   service_account_namespaces = ["tools"]                        # namespaces additionnels
  # },
]

Puis pousser : la CI tools/.gitea/workflows/vault.yaml applique le Terraform.

Ce que ça crée

Le module app_policy (appelé en for_each depuis main.tf) crée, par app :

Ressource Vault Nom À quoi ça sert
vault_jwt_auth_backend_role gitea_cicd_<app> Identité de la CI : la pipeline de l'app s'y authentifie (mount gitea_jwt, user_claim=email, bound_audiences=[gitea_app_id])
vault_policy (ops) <app>-ops Droits CI : créer postgres/roles/<app>*, auth/kubernetes/role/<app>*, éditer kvv2/<app>/*, lire les secrets de bootstrap google/gitea
vault_identity_group <app>-ops Groupe Vault rattachant les comptes Gitea à la policy ops
vault_policy (runtime) <app> Droits du pod : lire kvv2/data/<app>/* et postgres/creds/<app>*

Extraits clés :

resource "vault_jwt_auth_backend_role" "gitea_jwt_cicd" {
  backend        = data.vault_auth_backend.gitea_jwt.path   # "gitea_jwt"
  role_name      = "gitea_cicd_${local.name}"
  token_policies = concat(["default"], var.ops_policies)
  bound_audiences = [var.gitea_app_id]
  user_claim     = "email"
  role_type      = "jwt"
}

resource "vault_policy" "app" {            # policy runtime du pod
  name   = local.name                       # = "<app>"
  policy = data.vault_policy_document.app.hcl  # read kvv2/data/<app>/* + postgres/creds/<app>*
}

Prérequis plateforme (déjà là)

Ces fondations vivent dans tools/hashicorp-vault/iac/main.tf et n'ont pas à être recréées :

  • mounts kvv2 (KV v2), postgres (moteur de bases de données), transit (cache VSO), auth kubernetes ;
  • la connexion Vault→Postgres (vault_database_secret_backend_connection) qui se connecte à pgbouncer.tools:5432/postgres avec le compte credentials_editor (issu de l'étape 2) ;
  • var.gitea_app_id = l'id de l'application OAuth2 Gitea, réglé une fois au setup (gitea_oidc_auth.yml).

Pourquoi cette étape vient avant la CI de l'app

Important

C'est ici que gitea_cicd_<app> naît. Le iac/providers.tf de l'app (étape 5) et le step vault-action du workflow (étape 6) s'authentifient avec ce rôle. S'il n'existe pas encore, la toute première exécution de la CI de l'app échoue à l'authentification Vault. Appliquer cette étape (et l'étape 2) avant de pousser le iac/ de l'app.

Notes / contraintes

  • Découpage des privilèges : la policy <app>-ops (CI, large) est distincte de la policy <app> (runtime, en lecture seule sur ses propres secrets). Le pod ne peut jamais créer de rôles.
  • ops_policies permet d'octroyer à la CI des droits transverses (ex. cms lit un token Cloudflare R2 via factory__cf_r2_arcodange_tf).
  • 2. Base de données — fournit credentials_editor, réutilisé par la connexion Vault→Postgres.
  • 5. Terraform de l'app — s'authentifie avec gitea_cicd_<app> et crée creds/<app> + le rôle K8s <app>.
  • 6. Workflows CI — le step vault-action et tofu apply utilisent gitea_cicd_<app>.
  • 4. Chart Helm — le pod utilise la policy runtime <app> via son ServiceAccount.