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>
4.7 KiB
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>_role → credentials_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_lookup → pgbouncer_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:5432et 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 Vaultcreds/<app>) qui câblent ça. Ici, on ne fait qu'établir la base et le rôle propriétaire.
Notes / contraintes
credentials_editorest 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 modeauth_queryde pgbouncer ; elle estsecurity_defineret n'est exécutable que parpgbouncer_auth.
Related
- 3. Vault plateforme — la connexion Vault→Postgres réutilise
credentials_editor; à faire en parallèle. - 5. Terraform de l'app — le module
app_rolesfaitGRANT <app>_role TO …: il exige que<app>_roleexiste déjà (créé ici). - 4. Chart Helm — où la connexion
pgbouncer.tools+ creds dynamiques est configurée.