Files
kapteins-daagbok/docs/deployment/npm-security.md
T
elpatron f0c3cacb06 feat(analytics): Plausible über PLAUSIBLE_ENABLED und PLAUSIBLE_HOST steuerbar
Runtime-Konfiguration im Frontend-Container trennt Prod und Staging;
Staging deaktiviert Analytics standardmäßig.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-05 18:04:31 +02:00

2.8 KiB

Deployment: Nginx Proxy Manager & Security (Sprint 1)

Kapteins Daagbok läuft öffentlich unter https://kapteins-daagbok.eu/ (Produktion) und https://staging.kapteins-daagbok.eu/ (Staging) hinter Nginx Proxy Manager (NPM, z. B. 172.16.10.10) mit Upstream auf die App-VMs (10.0.0.25 Prod, 10.0.0.27 Staging).

NPM Proxy Host

Einstellung Wert
Domain kapteins-daagbok.eu / staging.kapteins-daagbok.eu
Scheme https
Forward Hostname / IP 10.0.0.25 (Prod) / 10.0.0.27 (Staging)
Forward Port 80 (Frontend-Nginx)
Websockets an, falls genutzt
Block Common Exploits an
SSL Let's Encrypt o. ä.

Custom Nginx (Advanced) — empfohlen

NPM setzt X-Forwarded-* in der Regel automatisch. Falls nicht, im Proxy-Host unter Advanced:

proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;

Backend-Umgebung (.env auf dem Server)

ORIGIN=https://kapteins-daagbok.eu
RP_ID=kapteins-daagbok.eu
SESSION_SECRET=<min. 32 Zeichen, openssl rand -base64 48>
# Docker Compose: Frontend-Nginx ist der direkte Proxy zum Backend → 1 Hop
TRUST_PROXY=1
# Nur bei direktem Backend-Zugriff ohne Frontend-Nginx: NPM-IP, z. B. TRUST_PROXY=172.16.10.10

ORIGIN muss exakt der Browser-URL entsprechen (ohne trailing slash).

Security-Header

  • HSTS, CSP (optional restriktiver): können in NPM unter „Custom Headers“ oder im Advanced-Block gesetzt werden.
  • Basis-Header für statische Dateien setzt client/nginx.conf.template via Container-Entrypoint (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, CSP optional inkl. Plausible).

Plausible Analytics

Konfiguration über .env (Frontend-Container):

PLAUSIBLE_ENABLED=true
PLAUSIBLE_HOST=https://plausible.elpatron.me

Staging-Default: PLAUSIBLE_ENABLED=false in docker-compose.staging.yml.

Script-Host wird in CSP (script-src, connect-src) nur bei PLAUSIBLE_ENABLED=true freigegeben. data-domain ist immer der aktuelle Hostname (Prod vs. Staging getrennt, wenn Staging aktiviert wird).

Nach Deploy prüfen

  1. https://kapteins-daagbok.eu/api/healthstatus: ok
  2. Passkey Login / Registrierung
  3. DevTools → Application → Cookie daagbok_session: Secure, HttpOnly, SameSite=Lax
  4. Response-Header auf index.html: CSP, X-Frame-Options
  5. Zwei Geräte hinter NAT: unabhängige Rate-Limits (nicht alle als eine IP)

Docker Compose

Keine Default-Passwörter in Produktion: starkes POSTGRES_PASSWORD (siehe postgres-password.md) und SESSION_SECRET in .env setzen (siehe .env.example).