The app ran as root, and the production image contained gcc, vim, wget
and gnupg. Code execution in the container would have allowed changing
the app code and fetching or compiling further tools.
- Multi-stage build: the wheels (psycopg2) are built in a build stage.
The runtime image only contains libpq5, postgresql-client and
wait-for-it (724 MB -> 298 MB).
- The app runs as the system user max (uid/gid 10001). The code is
owned by root and precompiled, the user has no writable directory.
- Log to stdout only; docker collects it. The log file under /app/log
was lost on every container recreation anyway.
- docker-compose: read-only filesystem (tmpfs for /tmp), no
capabilities, no-new-privileges and log rotation (5 x 10 MB).
- Add a .dockerignore and set MAX_ORIGIN in docker-compose.yml.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Anonymous users must not get cookies or session store entries, so a
CSRF token for /login is not possible. Instead, every unsafe request is
checked statelessly via Sec-Fetch-Site, falling back to Origin. Requests
without both headers are not from a browser and are accepted. The
expected origin comes from the new MAX_ORIGIN setting, which is required
outside of debug mode.
Further CSRF changes:
- Do not delete the CSRF token on a failed check. It added no security,
but invalidated forms in open tabs and could be triggered cross-site.
- Treat HEAD and OPTIONS as safe methods.
Keep the login page free of sessions:
- Show login messages via ?msg=login_required|logout (fixed keys only)
and the login error via the template context instead of flash().
- Only send the session deletion cookie if the client sent a cookie.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- scripts/prod: entrypoint, db-setup and schema.sql shipped in the image
- scripts/dev: seed/clear db, psql and cleanup, run natively in the
devcontainer instead of via docker-compose
- move setup.cfg to the repo root so linters pick it up
- Makefile targets run directly in the devcontainer
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>