fix(pgbouncer,vault) — ce qu'un client laisse sur une connexion, le suivant n'en hérite plus #25

Merged
arcodange merged 2 commits from arcodange/fuite-set-role into main 2026-07-29 20:22:14 +02:00
Showing only changes of commit 7d13a8d764 - Show all commits
+37 -4
View File
@@ -22,10 +22,20 @@ pgbouncer: &pgbouncer_config
# `DEALLOCATE ALL` ne nettoie QUE les requêtes préparées — d'où sa mise en # `DEALLOCATE ALL` ne nettoie QUE les requêtes préparées — d'où sa mise en
# place (07e2c6d, « prepared statement already exists » de crowdsec). # place (07e2c6d, « prepared statement already exists » de crowdsec).
# Tout le reste de l'état de session passait au suivant. MESURÉ contre un # 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) : # pgbouncer **1.23.1** — la version RÉELLEMENT déployée (image
# le client 2, qui n'avait rien demandé, héritait de `work_mem=17MB`, du # ghcr.io/icoretech/pgbouncer-docker:1.23.1-fixed, chart pgbouncer-2.3.1),
# `LISTEN canal_test` du client 1, de sa table TEMP — et de son `SET ROLE`, # montée avec CETTE configuration extraite du cluster (session,
# au point de créer des tables appartenant à un autre rôle que le sien. # 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.
#
# Portée de la fuite, mesurée et non supposée : les pools sont partitionnés
# par (base, utilisateur) — 210 pools, aucun ne mélange deux bases ni deux
# comptes. Le seul héritier possible d'un `SET ROLE` posé par kadans-api
# est un client de la base `kadans` avec le MÊME identifiant éphémère,
# c'est-à-dire kadans-api elle-même. Défaut d'hygiène, pas brèche
# inter-applications.
# #
# `DISCARD ALL` est le défaut de pgbouncer, et c'est un SUR-ENSEMBLE strict # `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` # des deux valeurs qui l'ont précédé ici : il contient `DEALLOCATE ALL`
@@ -37,6 +47,29 @@ pgbouncer: &pgbouncer_config
# la connexion serveur n'est rendue qu'à la déconnexion du client, donc le # 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é : # 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. # table TEMP, `SET work_mem` et `LISTEN` d'un client VIVANT survivent.
#
# ⚠⚠ CE QUE CETTE LIGNE REND FRAGILE — et c'est l'inverse de ce qu'on croit.
#
# `pool_mode` n'est PAS déclaré ici, donc il vaut `session`, le défaut.
# C'est ce qui rend `DISCARD ALL` sans danger. **Le jour où quelqu'un
# écrira `pool_mode: transaction`, il cassera kadans-api en silence** :
# son `SET ROLE` est posé UNE FOIS à l'ouverture (`AfterConnect`, db.go),
# et en transaction pooling `DISCARD ALL` court ENTRE deux transactions —
# donc le rôle est effacé avant les migrations suivantes. MESURÉ, les
# quatre combinaisons, propriétaire de la table créée :
#
# session + DEALLOCATE ALL → rôle stable ✅ (mais la fuite reste)
# session + DISCARD ALL → rôle stable ✅ ← ce qu'on déploie
# transaction + DEALLOCATE ALL → rôle stable ✅ (par ACCIDENT : la fuite
# qu'on referme est ce qui le sauvait)
# transaction + DISCARD ALL → rôle ÉPHÉMÈRE ❌ le défaut du 28/07,
# qui a mis l'API à terre une demi-journée
#
# Et `poserRoleProprietaire` ne peut pas le voir : sa relecture de
# `current_role` a lieu à l'ouverture, où le rôle est encore correct.
# Ce qui couvre ce cas, c'est la ceinture Vault (`ALTER ROLE … SET ROLE`,
# module app_roles) : un défaut de rôle survit à `DISCARD ALL`. **Avant de
# passer en transaction pooling, vérifier que les baux Vault ont tourné.**
server_reset_query: DISCARD ALL # défaut pgbouncer — ⚠ ne pas réduire : voir ci-dessus server_reset_query: DISCARD ALL # défaut pgbouncer — ⚠ ne pas réduire : voir ci-dessus
server_idle_timeout: 7200 server_idle_timeout: 7200
pgbouncerExporter: pgbouncerExporter: