Bound what a request can make the server hold

Cap JSON bodies at 64 KB on every API route except JMAP and uploads, which
bound themselves. Sign-in used to read a body of any size before its rate
limits ran; the flood ceiling now also runs before the body is read.

For sessions whose JMAP requests are checked, lower the read cap from 16 MB
to 4 MB, allow four such reads per session at once, and turn requests away
with a 503 once 32 MB is held across everyone.

Count sign-in limits per /64 for IPv6, since one host holds a whole /64.

Bind the compose example to loopback, and run it read-only with no
capabilities and no-new-privileges. Keep .env.* out of git and the image
build context.
This commit is contained in:
2026-09-16 07:54:20 -07:00
parent b2e7db938c
commit 98e105efd6
6 changed files with 244 additions and 15 deletions
+14 -1
View File
@@ -9,8 +9,21 @@ services:
BASE_PATH: ${BASE_PATH:-}
image: ihasmail:2
restart: unless-stopped
# Loopback only: ihasmail expects a TLS reverse proxy in front of it. On
# every interface the app is reachable over plain HTTP, passwords and all,
# and with TRUST_PROXY any machine on a private network can set its own
# X-Forwarded-For. A proxy running in Docker can reach the service by name
# on the compose network and needs no published port at all.
ports:
- "8080:8080"
- "127.0.0.1:8080:8080"
# The app needs no privileges and writes only to /data and /tmp.
read_only: true
tmpfs:
- /tmp
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
environment:
STALWART_URL: ${STALWART_URL:?set STALWART_URL in .env}
APP_SECRET: ${APP_SECRET:?set APP_SECRET in .env (openssl rand -base64 48)}