fix(pgbouncer,vault) — ce qu'un client laisse sur une connexion, le suivant n'en hérite plus #25
@@ -30,9 +30,36 @@ resource "vault_database_secret_backend_role" "role" {
|
||||
backend = local.vault_mount_postgres.path
|
||||
name = local.instance
|
||||
db_name = "postgres"
|
||||
# ── Le rôle ÉPHÉMÈRE endosse le rôle STABLE, dès le login ───────────────────
|
||||
#
|
||||
# En PostgreSQL, un objet appartient au rôle qui l'a CRÉÉ. Comme chaque
|
||||
# démarrage de pod obtient un rôle `v-kubernet-…` neuf, toute migration crée
|
||||
# des objets que le pod SUIVANT ne peut plus lire. C'est ce qui a mis l'API
|
||||
# kadans à terre une demi-journée le 2026-07-28 (« permission denied for table
|
||||
# qualification_video »), et c'est ce que le CronJob `pg-fix-table-ownership`
|
||||
# rattrape tous les jours à 03:00 — a posteriori, et pour les seules TABLES.
|
||||
#
|
||||
# `ALTER ROLE … SET ROLE` fait de l'endossement un DÉFAUT DE CONNEXION : plus
|
||||
# rien à poser côté application, et ça vaut aussi pour les clients qui ne sont
|
||||
# pas l'application (le `psql` d'un job, une console d'exploitation).
|
||||
#
|
||||
# AUCUN privilège nouveau : le `GRANT` de la ligne précédente rend déjà le
|
||||
# rôle éphémère MEMBRE du rôle stable. Endosser une casquette qu'on porte
|
||||
# déjà, ce n'est pas une élévation — et c'est pourquoi cette instruction ne
|
||||
# peut pas échouer là où le `GRANT` réussit.
|
||||
#
|
||||
# MESURÉ (PostgreSQL 16, compte CREATEROLE non-superutilisateur, comme celui
|
||||
# de Vault) : l'instruction passe, le login donne `session_user=v-test-1` /
|
||||
# `current_role=proprio_v`, et un `ALTER ROLE … RESET role` la retire.
|
||||
# Vérifié aussi qu'elle SURVIT à `RESET ALL` / `DISCARD ALL` — donc elle
|
||||
# compose avec le `server_reset_query` de pgbouncer au lieu de s'y opposer.
|
||||
#
|
||||
# ⚠ Ne vaut que pour les identifiants créés APRÈS l'apply : les baux en cours
|
||||
# gardent leur ancien comportement jusqu'à leur renouvellement.
|
||||
creation_statements = [
|
||||
"CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';",
|
||||
"GRANT ${local.owner_role} TO \"{{name}}\";",
|
||||
"ALTER ROLE \"{{name}}\" SET ROLE ${local.owner_role};",
|
||||
]
|
||||
revocation_statements = [
|
||||
"REASSIGN OWNED BY \"{{name}}\" TO ${local.owner_role};", # reassign must be executed in the database where the reassgined objects are - TODO (one connection per database/app)
|
||||
|
||||
+24
-1
@@ -14,7 +14,30 @@ pgbouncer: &pgbouncer_config
|
||||
auth_type: scram-sha-256
|
||||
auth_query: SELECT uname, phash FROM user_lookup($1)
|
||||
ignore_startup_parameters: extra_float_digits # unsupported jdbc extra_float_digits=2 argument
|
||||
server_reset_query: DEALLOCATE ALL # fix prepared statement already exist (crowdsec)
|
||||
# Ce pgbouncer est PARTAGÉ (crowdsec, plausible, kadans, + le compte que
|
||||
# Vault utilise sur la base `postgres`). Ce qu'un client laisse derrière
|
||||
# lui sur une connexion serveur, le client SUIVANT en hérite : le reset
|
||||
# est la SEULE barrière entre deux clients d'un même pool.
|
||||
#
|
||||
# `DEALLOCATE ALL` ne nettoie QUE les requêtes préparées — d'où sa mise en
|
||||
# place (07e2c6d, « prepared statement already exists » de crowdsec).
|
||||
# Tout le reste de l'état de session passait au suivant. MESURÉ contre un
|
||||
# pgbouncer 1.25.2 monté avec CETTE configuration (session, pool_size=1) :
|
||||
# le client 2, qui n'avait rien demandé, héritait de `work_mem=17MB`, du
|
||||
# `LISTEN canal_test` du client 1, de sa table TEMP — et de son `SET ROLE`,
|
||||
# au point de créer des tables appartenant à un autre rôle que le sien.
|
||||
#
|
||||
# `DISCARD ALL` est le défaut de pgbouncer, et c'est un SUR-ENSEMBLE strict
|
||||
# des deux valeurs qui l'ont précédé ici : il contient `DEALLOCATE ALL`
|
||||
# (donc le correctif crowdsec est conservé — vérifié : deux clients
|
||||
# successifs préparent le même nom sans erreur) ET
|
||||
# `SELECT pg_advisory_unlock_all()` (la valeur que pose le sous-chart).
|
||||
#
|
||||
# Il ne peut RIEN casser pour un client vivant : en `pool_mode = session`
|
||||
# la connexion serveur n'est rendue qu'à la déconnexion du client, donc le
|
||||
# reset ne court jamais entre deux requêtes d'une même session. Vérifié :
|
||||
# table TEMP, `SET work_mem` et `LISTEN` d'un client VIVANT survivent.
|
||||
server_reset_query: DISCARD ALL # défaut pgbouncer — ⚠ ne pas réduire : voir ci-dessus
|
||||
server_idle_timeout: 7200
|
||||
pgbouncerExporter:
|
||||
enabled: false
|
||||
|
||||
Reference in New Issue
Block a user