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

94 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
[Factory](../../../README.md) > [Doc](../../README.md) > [Runbooks](../README.md) > [Nouvelle application web](README.md) > **2. Base de données**
# 2. Provisionner la base de données
> **Status:** ✅ Active
> **Upstream:** [1. Dépôt Gitea](01-gitea-repo.md)
> **Downstream:** [5. Terraform de l'app](05-app-terraform.md)
> **Related:** [3. Vault plateforme](03-vault-platform.md) · [4. Chart Helm](04-helm-chart.md) · [Conventions de nommage](conventions.md)
---
## 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](04-helm-chart.md)) qui héritent de `<app>_role`.
## Action
Ajouter `"<app>"` au set `applications` de [`factory/postgres/iac/terraform.tfvars`](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/postgres/iac/terraform.tfvars) :
```hcl
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`](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/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é :
```hcl
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`](https://gitea.arcodange.lab/arcodange-org/factory/src/branch/main/.gitea/workflows/postgres.yaml) se déclenche sur tout changement de `postgres/**/*.tf` ou `*.tfvars` :
```mermaid
%%{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](03-vault-platform.md)).
- 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`.
## Related
- [3. Vault plateforme](03-vault-platform.md) — la connexion Vault→Postgres réutilise `credentials_editor` ; à faire en parallèle.
- [5. Terraform de l'app](05-app-terraform.md) — le module `app_roles` fait `GRANT <app>_role TO …` : il **exige** que `<app>_role` existe déjà (créé ici).
- [4. Chart Helm](04-helm-chart.md) — où la connexion `pgbouncer.tools` + creds dynamiques est configurée.