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>
94 lines
4.7 KiB
Markdown
94 lines
4.7 KiB
Markdown
[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.
|