11 Commits
Author SHA1 Message Date
finnandClaude Opus 5.5 c72bc8c6af Disable the gunicorn control socket in the Docker image
Gunicorn 25.1+ creates a control socket under $HOME/.gunicorn, which
fails in the read-only container without a home directory and logs
"Control server error". The socket is not used.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 19:19:05 +00:00
finnandClaude Opus 5.5 f46a346078 Show the version in a footer for logged-in users
The version is read once at startup from the VERSION file, which is now
also copied into the Docker image. The footer is right-aligned below the
content block and only shown to logged-in users, so anonymous visitors
do not learn the exact version.

The startup log now has "Max starting." at the beginning of main() and
"Max initialized, version X." at the end, so a failed startup is visible
by the missing second line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 17:43:02 +00:00
finnandClaude Opus 5.5 2b1160862a Fix several small security and robustness issues
Login:
- One message for unknown email, wrong password and deactivated
  account. Before, "This account is not enabled!" only appeared after
  the right password and confirmed valid credentials.
- Email and password must be non-empty strings. A missing password
  field became the string "None" and logged in a user with that
  password.

Forms:
- "Create user" no longer renders the password back into the HTML after
  a validation error.
- Password fields for a new password use autocomplete="new-password",
  so a password manager does not fill in the admin's own password. The
  email fields on the create pages use autocomplete="off".
- Email addresses are limited to 64 characters in the local part and
  254 in total (RFC 5321). Longer ones caused a database error and
  HTTP 500.
- alerts.html no longer translates flash messages a second time. Running
  str.format() again on an already formatted message was fragile and
  logged "Unknown translation" for every German message. All flashes
  now translate when flashing, "Invalid data" included.

Debug mode:
- The Docker image sets MAX_PRODUCTION=1, and the app refuses to start
  in debug mode then. FLASK_DEBUG=1 in production would otherwise enable
  the fixed secret key, cookies without Secure, no HSTS and query
  arguments (including password hashes) in the log.
- make run-dev and run-dev-as-prod bind to 127.0.0.1.

Other:
- A denied access is logged with the logger at debug level instead of
  print. PermissionDenied loses its unused message, it is deliberately
  answered with a 404.
- Fix the typo in /user/<id>/ser-toggle-can-use-different-alias-domain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 20:04:22 +00:00
finnandClaude Opus 5.5 931243dd60 Harden the container: non-root, no build tools, read-only, stdout logging
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>
2026-10-21 18:09:30 +00:00
finnandClaude Opus 5.5 9895017385 Restructure scripts into dev/ and prod/ for the devcontainer
- 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>
2026-10-21 08:39:28 +00:00
finnandClaude Opus 5.5 7c2b9fb3f4 Refactor logging and drop deprecated FLASK_ENV
Move logging setup into max/log.py. The file handler is only used outside
debug mode and its directory is configurable via LOG_DIR. FLASK_ENV is
deprecated in Flask; debug mode is now controlled via FLASK_DEBUG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 08:21:07 +00:00
finn 2bd853e9b5 modernizing the dependencies 2026-10-19 18:53:59 +02:00
Finn Stutzenstein b31f83e214 Fix prod docker file 2022-03-06 09:39:06 +01:00
Finn Stutzenstein aff7bf3b48 Unique emails and added can_receive 2022-03-06 09:34:23 +01:00
Finn Stutzenstein add153d8a6 More work 2022-01-22 18:37:56 +01:00
FinnStutzenstein 9d018e00be initial commit 2020-04-13 08:31:44 +02:00