diff --git a/README.md b/README.md index cbcf9e8..a1af264 100644 --- a/README.md +++ b/README.md @@ -111,8 +111,8 @@ A self-hosted personal life administration app with a dark-themed UI. Manage you - **Non-root containers** — both backend (`appuser:1000`) and frontend (`nginx-unprivileged`) run as non-root - **No external backend port** — port 8000 is internal-only; all traffic flows through nginx - **Server version suppression** — `server_tokens off` (nginx) and `--no-server-header` (uvicorn) -- **Rate limiting** — nginx `limit_req_zone` (10 req/min, burst=5) on `/api/auth/login`, `/verify-password`, `/change-password`, `/totp-verify` -- **Application-level rate limiting** — in-memory IP-based rate limit (5 attempts / 5 min) + DB-backed account lockout (10 failures = 30-min lock) +- **Rate limiting** — nginx `limit_req_zone` (10 req/min) on `/api/auth/login` (burst=5), `/verify-password` (burst=5), `/change-password` (burst=5), `/totp-verify` (burst=5), `/setup` (burst=3) +- **DB-backed account lockout** — 10 failed attempts triggers 30-minute lock per account - **Dotfile blocking** — `/.env`, `/.git/config`, etc. return 404 (`.well-known` preserved for ACME) - **CSP headers** — Content-Security-Policy on all responses, scoped for Google Fonts - **CORS** — configurable origins with explicit method/header allowlists diff --git a/backend/app/services/ntfy.py b/backend/app/services/ntfy.py index 402ede0..3fb4f2b 100644 --- a/backend/app/services/ntfy.py +++ b/backend/app/services/ntfy.py @@ -19,7 +19,7 @@ NTFY_TIMEOUT = 8.0 # seconds — hard cap to prevent hung requests # Loopback, link-local, and all RFC 1918 private ranges are blocked to prevent # SSRF against Docker-internal services. If a self-hosted ntfy server on the LAN -# is required, set ALLOW_LAN_NTFY=true in .env and document the accepted risk. +# is required, remove the RFC 1918 entries from _BLOCKED_NETWORKS and document the accepted risk. _BLOCKED_NETWORKS = [ ipaddress.ip_network("127.0.0.0/8"), # IPv4 loopback ipaddress.ip_network("10.0.0.0/8"), # RFC 1918 private diff --git a/frontend/nginx.conf b/frontend/nginx.conf index 220567c..3de626d 100644 --- a/frontend/nginx.conf +++ b/frontend/nginx.conf @@ -47,6 +47,7 @@ server { } location /api/auth/setup { + # Tighter burst: setup is one-time-only, 3 attempts is sufficient limit_req zone=auth_limit burst=3 nodelay; limit_req_status 429; include /etc/nginx/proxy-params.conf;