Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f53c3adb7d |
@@ -30,36 +30,9 @@ resource "vault_database_secret_backend_role" "role" {
|
|||||||
backend = local.vault_mount_postgres.path
|
backend = local.vault_mount_postgres.path
|
||||||
name = local.instance
|
name = local.instance
|
||||||
db_name = "postgres"
|
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 = [
|
creation_statements = [
|
||||||
"CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';",
|
"CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';",
|
||||||
"GRANT ${local.owner_role} TO \"{{name}}\";",
|
"GRANT ${local.owner_role} TO \"{{name}}\";",
|
||||||
"ALTER ROLE \"{{name}}\" SET ROLE ${local.owner_role};",
|
|
||||||
]
|
]
|
||||||
revocation_statements = [
|
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)
|
"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)
|
||||||
|
|||||||
+1
-57
@@ -14,63 +14,7 @@ pgbouncer: &pgbouncer_config
|
|||||||
auth_type: scram-sha-256
|
auth_type: scram-sha-256
|
||||||
auth_query: SELECT uname, phash FROM user_lookup($1)
|
auth_query: SELECT uname, phash FROM user_lookup($1)
|
||||||
ignore_startup_parameters: extra_float_digits # unsupported jdbc extra_float_digits=2 argument
|
ignore_startup_parameters: extra_float_digits # unsupported jdbc extra_float_digits=2 argument
|
||||||
# Ce pgbouncer est PARTAGÉ (crowdsec, plausible, kadans, + le compte que
|
server_reset_query: DEALLOCATE ALL # fix prepared statement already exist (crowdsec)
|
||||||
# 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.23.1** — la version RÉELLEMENT déployée (image
|
|
||||||
# ghcr.io/icoretech/pgbouncer-docker:1.23.1-fixed, chart pgbouncer-2.3.1),
|
|
||||||
# montée avec CETTE configuration extraite du cluster (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.
|
|
||||||
#
|
|
||||||
# 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
|
|
||||||
# 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.
|
|
||||||
#
|
|
||||||
# ⚠⚠ 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_idle_timeout: 7200
|
server_idle_timeout: 7200
|
||||||
pgbouncerExporter:
|
pgbouncerExporter:
|
||||||
enabled: false
|
enabled: false
|
||||||
|
|||||||
+30
-7
@@ -140,13 +140,36 @@ redis: &redis_config
|
|||||||
runAsUser: 999
|
runAsUser: 999
|
||||||
|
|
||||||
# -- Compute resources used by the container. More info [here](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/).
|
# -- Compute resources used by the container. More info [here](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/).
|
||||||
resources: {}
|
#
|
||||||
# limits:
|
# ⚠ CE N'EST PAS DU CONFORT — c'est ce qui empêche Redis de mourir en boucle.
|
||||||
# cpu: 100m
|
#
|
||||||
# memory: 128Mi
|
# Le sous-chart FIGE ses sondes à `timeoutSeconds: 1` sur `redis-cli ping`, et
|
||||||
# requests:
|
# n'expose aucune valeur pour les surcharger. Sur ce matériel (Raspberry Pi),
|
||||||
# cpu: 100m
|
# un conteneur SANS réservation tombe dans la classe QoS `BestEffort` : il est
|
||||||
# memory: 128Mi
|
# le premier affamé quand le nœud est chargé, et le simple lancement de
|
||||||
|
# `redis-cli` y dépasse la seconde.
|
||||||
|
#
|
||||||
|
# Mesuré le 2026-07-29 sur `redis-0` : « Liveness probe failed: command timed
|
||||||
|
# out: "redis-cli ping" timed out after 1s » **836 fois en 23 jours**, 135
|
||||||
|
# redémarrages, le conteneur tué par SIGTERM après ~90 s de vie à chaque tour.
|
||||||
|
#
|
||||||
|
# ⚠ CE QUE ÇA CASSAIT, ET QUI N'AVAIT RIEN À VOIR AVEC REDIS EN APPARENCE : le
|
||||||
|
# plugin crowdsec de Traefik utilise ce Redis comme cache de décisions. Cache
|
||||||
|
# injoignable ⇒ `isCrowdsecStreamHealthy:false` ⇒ le plugin REFUSE PAR DÉFAUT
|
||||||
|
# tout client non listé dans `clientTrustedIPs` ⇒ **403, corps vide, sur
|
||||||
|
# `kadans.arcodange.fr` et `gitea.arcodange.fr`**, pendant que les hôtes qui ne
|
||||||
|
# portent pas ce middleware répondaient normalement. Le symptôme ne nomme ni
|
||||||
|
# Redis, ni crowdsec, ni la sonde — il ressemble à un bannissement d'IP, et
|
||||||
|
# c'est par là qu'on cherche d'abord.
|
||||||
|
#
|
||||||
|
# Une réservation fait passer le conteneur en `Burstable` et lui garantit sa
|
||||||
|
# part. Les limites restent larges : Redis n'est pas ce qui sature ce nœud.
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 50m
|
||||||
|
memory: 64Mi
|
||||||
|
limits:
|
||||||
|
memory: 256Mi
|
||||||
|
|
||||||
# -- Pod-level affinity. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling).
|
# -- Pod-level affinity. More info [here](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-v1/#scheduling).
|
||||||
affinity: {}
|
affinity: {}
|
||||||
|
|||||||
Reference in New Issue
Block a user