Files
factory/doc/runbooks/new-web-app/02-database.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

4.7 KiB
Raw Permalink Blame History

Factory > Doc > Runbooks > Nouvelle application web > 2. Base de données

2. Provisionner la base de données

Status: Active Upstream: 1. Dépôt Gitea Downstream: 5. Terraform de l'app Related: 3. Vault plateforme · 4. Chart Helm · Conventions de nommage


Summary

La base de l'app et son rôle propriétaire <app>_role sont créés par un Terraform côté plateforme (factory/postgres/iac), pas dans le dépôt de l'app. On ajoute simplement le nom de l'app à une liste, et la CI de factory applique. L'app, elle, ne se connectera jamais avec un mot de passe statique : elle obtiendra des identifiants éphémères de Vault (cf. étape 4) qui héritent de <app>_role.

Action

Ajouter "<app>" au set applications de factory/postgres/iac/terraform.tfvars :

applications = [
    "webapp",
    "erp",
    "crowdsec",
    "plausible",
    "dance-lessons-coach",
    "<app>",   # ← ajouter
]

Puis pousser : la CI applique automatiquement (voir plus bas).

Ce que ça crée

postgres/iac/main.tf itère for_each sur le set et crée, par app :

Ressource Nom Rôle
postgresql_role <app>_role Rôle non-login, propriétaire de la base
postgresql_grant_role <app>_rolecredentials_editor (WITH ADMIN OPTION) Laisse Vault rattacher les users dynamiques à ce rôle
postgresql_database <app> La base (owner <app>_role, template0, alter_object_ownership)
postgresql_function user_lookup() (dans la base <app>) Authentification pgbouncer (lit pg_shadow)
postgresql_grant EXECUTE sur user_lookuppgbouncer_auth Autorise pgbouncer à résoudre les users

Extrait clé :

resource "postgresql_role" "app_role" {
  for_each = var.applications
  name     = "${each.value}_role"
  login    = false               # non-login : ne sert que de "porteur de droits"
}
resource "postgresql_database" "app_db" {
  for_each               = var.applications
  name                   = each.value
  owner                  = postgresql_role.app_role[each.value].name
  template               = "template0"
  alter_object_ownership = true
}

Comment c'est appliqué

Le workflow factory/.gitea/workflows/postgres.yaml se déclenche sur tout changement de postgres/**/*.tf ou *.tfvars :

%%{init: {'theme': 'base'}}%%
flowchart LR
    classDef ci fill:#059669,stroke:#047857,color:#fff
    classDef db fill:#2563eb,stroke:#1e40af,color:#fff
    PUSH["push tfvars"]:::ci --> JWT["OIDC Gitea → JWT Vault<br>(role gitea_cicd)"]:::ci
    JWT --> READ["lit kvv1/postgres/credentials<br>→ TF_VAR_postgres_*"]:::ci
    READ --> APPLY["tofu apply postgres/iac"]:::ci
    APPLY --> DB["base app + app_role"]:::db

Le provider PostgreSQL pointe l'hôte 192.168.1.202 (sslmode=disable, superuser=true) et s'authentifie avec le compte credentials_editor, dont les identifiants sont dans Vault à kvv1/postgres/credentials_editor/credentials.

Le modèle de connexion (à retenir)

Important

L'application ne se connecte pas directement à Postgres avec un user fixe. Elle vise pgbouncer.tools:5432 et utilise des users dynamiques courts émis par Vault, qui héritent de <app>_role (donc des droits sur la base <app>). C'est l'étape 4 (chart + VSO) et l'étape 5 (rôle Vault creds/<app>) qui câblent ça. Ici, on ne fait qu'établir la base et le rôle propriétaire.

Notes / contraintes

  • credentials_editor est un compte unique partagé par toutes les apps, à fort privilège (il peut créer des rôles). Il sert aussi de compte de connexion au moteur Postgres de Vault (cf. étape 3).
  • La fonction user_lookup() est indispensable au mode auth_query de pgbouncer ; elle est security_definer et n'est exécutable que par pgbouncer_auth.
  • 3. Vault plateforme — la connexion Vault→Postgres réutilise credentials_editor ; à faire en parallèle.
  • 5. Terraform de l'app — le module app_roles fait GRANT <app>_role TO … : il exige que <app>_role existe déjà (créé ici).
  • 4. Chart Helm — où la connexion pgbouncer.tools + creds dynamiques est configurée.