Files
Claude 49618c57bf Faire poser le rôle applicatif par l'application elle-même
`db-init` suppose un orchestrateur qui honore
`depends_on: service_completed_successfully`. Swarm l'ignore, et une pile
déployée avant l'ajout du service ne le contient même pas. L'application
redémarrait alors en boucle sur un refus d'authentification que le
diagnostic ajouté précédemment décrivait sans que personne puisse le
corriger.

Elle le corrige donc elle-même au démarrage, si on lui confie les
identifiants d'amorçage — et se tait sinon, pour ne pas contrarier un
déploiement qui préfère les garder hors du conteneur applicatif. Le point
d'entrée retire ces variables avant de lancer le serveur : le processus qui
sert les requêtes ne les voit jamais.

Le branchement create/alter se fait côté client et non dans un bloc `DO` :
le corps d'un `DO` est une chaîne littérale, où `$1` n'est pas un paramètre
de requête. L'échappement du mot de passe est confié à `quote_literal`, et
le nom de rôle est refusé s'il n'a pas la forme d'un identifiant.

Éprouvé sous authentification scram réelle : après réalignement, l'ancien
mot de passe est refusé et le nouveau accepté, le rôle reste NOSUPERUSER
NOBYPASSRLS et devient propriétaire de la base.

Au passage, `withTenant` pose un budget de transaction explicite. Tout accès
aux données passe par lui, si bien que le défaut Prisma de 5 s plafonnait en
réalité chaque requête de l'application, et l'échec se présentait en `P2028`
qui ne désigne ni la requête ni la cause.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv
2026-08-09 18:32:38 +00:00

118 lines
5.5 KiB
Bash
Executable File
Raw Permalink 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.
#!/bin/sh
set -eu
# Démarrage du conteneur applicatif.
#
# La clé de chiffrement protège le NIR, l'IBAN, les secrets de second facteur et
# les pièces du dossier salarié. Elle doit exister avant que quoi que ce soit
# démarre — mais l'exiger en variable d'environnement rendait tout déploiement
# impossible sans une étape manuelle, et une variable d'environnement n'est de
# toute façon pas un bon coffre : elle s'affiche dans `docker inspect` et dans
# l'interface de gestion.
#
# Elle est donc produite au premier démarrage et conservée dans un volume
# **distinct** de la base et des documents : une sauvegarde de l'un ne doit pas
# emporter la clé de l'autre.
#
# `ENCRYPTION_KEY` fournie explicitement l'emporte toujours : un déploiement qui
# gère ses secrets par ailleurs ne doit pas être contrarié.
KEY_FILE="${ENCRYPTION_KEY_FILE:-/secrets/encryption.key}"
# Surchargeable pour pouvoir éprouver ce script hors d'une image.
MIGRATOR_DIR="${MIGRATOR_DIR:-/migrator}"
if [ -z "${ENCRYPTION_KEY:-}" ]; then
if [ -f "$KEY_FILE" ]; then
ENCRYPTION_KEY="$(cat "$KEY_FILE")"
else
mkdir -p "$(dirname "$KEY_FILE")"
ENCRYPTION_KEY="$(node -e "process.stdout.write(require('node:crypto').randomBytes(32).toString('base64'))")"
# Écriture puis restriction : le fichier ne doit jamais être lisible par
# d'autres, même brièvement.
(umask 077; printf '%s' "$ENCRYPTION_KEY" > "$KEY_FILE")
echo ''
echo '════════════════════════════════════════════════════════════════'
echo ' Clé de chiffrement produite au premier démarrage.'
echo ''
echo " $ENCRYPTION_KEY"
echo ''
echo ' Conservez-la hors de ce serveur. Sans elle, le NIR, les IBAN et'
echo ' les pièces du dossier salarié sont définitivement illisibles.'
echo ''
echo " Elle est stockée dans $KEY_FILE, sur un volume distinct de la"
echo ' base et des documents — à sauvegarder séparément.'
echo '════════════════════════════════════════════════════════════════'
echo ''
fi
fi
export ENCRYPTION_KEY
# Pose ou réaligne le rôle applicatif, si les identifiants d'amorçage sont
# fournis. Sans eux, ce script se tait : c'est alors `db-init` qui s'en charge.
node "${BOOTSTRAP_SCRIPT:-./docker/bootstrap-role.mjs}" || true
# Les identifiants d'amorçage ne vont pas plus loin : le serveur qui traite les
# requêtes ne doit pas les avoir sous la main. Une exécution de code arbitraire
# dans l'application n'y donnera pas accès, et l'isolation par la row-level
# security garde son sens.
unset POSTGRES_PASSWORD PGPASSWORD
# Les migrations s'appliquent au démarrage : l'image se déploie sans étape
# séparée.
#
# Depuis /migrator, arbre séparé de celui de l'application : la CLI y trouve ses
# propres dépendances, et le fichier de configuration y résout `prisma/config`.
# Appelée par son chemin plutôt que par `.bin/prisma` — un lien symbolique
# recopié d'une image à l'autre est une dépendance de plus à la disposition des
# fichiers, et c'est exactement ce qui a cassé ici.
migration_log="$(mktemp)"
if ! ( cd "$MIGRATOR_DIR" && node node_modules/prisma/build/index.js migrate deploy ) \
>"$migration_log" 2>&1; then
cat "$migration_log"
# Deux codes, deux causes voisines :
# P1010 — le rôle n'existe pas ;
# P1000 — il existe, mais son mot de passe ne correspond pas à celui de la
# pile, typiquement parce qu'il a été posé lors d'un déploiement
# antérieur avec une autre valeur.
#
# Les voir **ici** signifie que l'amorçage ci-dessus n'a pas fait son travail :
# soit il n'a pas reçu d'identifiants, soit il a échoué. Le message renvoie
# donc à ce qu'il a journalisé, et non à une manœuvre à improviser.
if grep -qE 'P1000|P1010' "$migration_log"; then
role="${APP_DB_USER:-planflow_app}"
echo ''
echo '════════════════════════════════════════════════════════════════'
if grep -q 'P1010' "$migration_log"; then
echo " Le rôle « ${role} » n'existe pas dans cette base."
else
echo " Le rôle « ${role} » existe, mais son mot de passe ne correspond"
echo ' pas à celui que porte la pile. Il a probablement été créé lors'
echo " d'un déploiement antérieur, avec une autre valeur."
fi
echo ''
echo " L'application sait poser ce rôle elle-même au démarrage. Les lignes"
echo ' « [bootstrap] » plus haut disent pourquoi elle ne l’a pas fait :'
echo ''
echo " • « Aucun identifiant d’amorçage fourni » — le service applicatif"
echo ' n’a pas POSTGRES_PASSWORD. Ajoutez-le, ou lancez :'
echo ' docker compose run --rm db-init'
echo ''
echo ' • « Provisionnement impossible » — le message qui suit indique'
echo ' ce qui a échoué (base injoignable, mot de passe d’amorçage'
echo ' erroné, droits insuffisants).'
echo '════════════════════════════════════════════════════════════════'
echo ''
fi
rm -f "$migration_log"
exit 1
fi
cat "$migration_log"
rm -f "$migration_log"
exec node server.js