Trois problèmes, dont un grave, trouvés en corrigeant un échec de déploiement. **Le compose connectait l'application en superutilisateur PostgreSQL.** Un superutilisateur contourne toute politique de sécurité au niveau ligne, y compris déclarée en FORCE : la seconde couche d'isolation était présente en base et absente des faits. Le README l'interdisait déjà noir sur blanc ; le chemin de déploiement que nous livrons faisait exactement l'inverse. Un script d'initialisation crée désormais un rôle `planflow_app` NOSUPERUSER NOBYPASSRLS, propriétaire de la base — il lui faut ce droit pour migrer, et les politiques sont en FORCE précisément pour s'appliquer aussi au propriétaire. Mesuré : en superutilisateur, deux lignes visibles sans compte courant ; avec le rôle dédié, zéro. Basculer la base de développement sur ce même rôle a révélé le défaut que le superutilisateur masquait : `resolveSession` lisait `Membership`, table filtrée par compte, sans périmètre. Avec la RLS active, plus personne ne pouvait se connecter. La résolution passe maintenant par une porte étroite — une politique qui n'ouvre que les lignes dont l'utilisateur est titulaire, sous `app.user_id` — le temps de trouver le compte, puis repasse par le périmètre ordinaire. La suite de tests traverse enfin la RLS au lieu de la contourner. **Les pièces du dossier salarié n'avaient aucun volume.** Elles étaient écrites dans la couche du conteneur et disparaissaient au premier redéploiement. Une pièce d'identité perdue ne se reconstitue pas. Le reste répond à la demande : Postgres préconfiguré — la base n'étant ni publiée ni attachée au réseau du proxy, ce mot de passe protège d'un conteneur voisin, pas d'Internet — réseau `nginx_default` déclaré externe avec l'application seule dessus, et port publié peu courant. ENCRYPTION_KEY reste la seule variable sans valeur par défaut, et n'en aura pas : elle chiffre le NIR, l'IBAN et les arrêts de travail. Une clé livrée avec l'image serait connue de quiconque lit ce dépôt. Au démarrage, l'application contrôle ses propres privilèges : elle refuse de se lancer si la base porte plus d'un compte, et se contente d'un avertissement s'il n'y en a qu'un — bloquer une installation mono-compte fermerait l'accès de l'entreprise à ses données pour une fuite entre clients qui ne peut pas se produire. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cr9dkEHwbDgkWPnyGj1Rjv
48 lines
1.8 KiB
Docker
48 lines
1.8 KiB
Docker
# syntax=docker/dockerfile:1
|
|
|
|
FROM node:22-alpine AS base
|
|
RUN corepack enable
|
|
WORKDIR /app
|
|
|
|
# ---- dependencies -----------------------------------------------------------
|
|
FROM base AS deps
|
|
COPY package.json pnpm-lock.yaml ./
|
|
COPY prisma ./prisma
|
|
RUN pnpm install --frozen-lockfile
|
|
|
|
# ---- build ------------------------------------------------------------------
|
|
FROM base AS build
|
|
COPY --from=deps /app/node_modules ./node_modules
|
|
COPY . .
|
|
RUN pnpm db:generate && pnpm build
|
|
|
|
# ---- runtime ----------------------------------------------------------------
|
|
FROM base AS runner
|
|
ENV NODE_ENV=production
|
|
|
|
RUN addgroup --system --gid 1001 nodejs \
|
|
&& adduser --system --uid 1001 --ingroup nodejs nextjs
|
|
|
|
# `output: standalone` emits a server bundle carrying only the modules it uses.
|
|
COPY --from=build --chown=nextjs:nodejs /app/.next/standalone ./
|
|
COPY --from=build --chown=nextjs:nodejs /app/.next/static ./.next/static
|
|
COPY --from=build --chown=nextjs:nodejs /app/public ./public
|
|
|
|
# Migrations run at startup, so the image can be deployed without a separate
|
|
# migration step. prisma/ and its CLI are needed for that.
|
|
COPY --from=build --chown=nextjs:nodejs /app/prisma ./prisma
|
|
COPY --from=build --chown=nextjs:nodejs /app/node_modules/prisma ./node_modules/prisma
|
|
COPY --from=build --chown=nextjs:nodejs /app/node_modules/.bin/prisma ./node_modules/.bin/prisma
|
|
COPY --from=build --chown=nextjs:nodejs /app/node_modules/@prisma ./node_modules/@prisma
|
|
|
|
# Créé dans l'image, et non laissé au montage : un volume nommé hérite du
|
|
# propriétaire du répertoire qu'il recouvre, et sans cela l'application —
|
|
# qui ne tourne pas en root — ne pourrait pas y écrire.
|
|
RUN mkdir -p /data/documents && chown -R nextjs:nodejs /data
|
|
|
|
USER nextjs
|
|
EXPOSE 3000
|
|
ENV PORT=3000 HOSTNAME=0.0.0.0
|
|
|
|
CMD ["sh", "-c", "./node_modules/.bin/prisma migrate deploy && node server.js"]
|