Caddy-Stack (docker/) auf Stand gebracht statt entfernt
Der Stack bleibt bewusst erhalten: Er richtet sich an Betreiber OHNE eigenen Reverse-Proxy, weil Caddy das Zertifikat selbst holt und erneuert. Er war allerdings seit Februar stehengeblieben und reichte zehn Variablen nicht an den Container durch - teils sicherheitsrelevant. Behoben: - HTTPS_ENABLED ergaenzt (Default true, Caddy terminiert TLS). Fehlte bisher komplett, dadurch waere der Refresh-Cookie OHNE Secure-Attribut gesetzt worden und trust proxy falsch gewesen. - JWT_EXPIRES_IN Default von 7d auf 15m korrigiert - der Wert galt dem ACCESS-Token und stammte aus der Zeit vor dem Access-/Refresh-Pattern. Ein Access-Token mit einer Woche Lebensdauer im Browser-Speicher macht das XSS-Fenster unnoetig gross. - JWT_REFRESH_EXPIRES_IN, CORS_ORIGINS, LISTEN_ADDR, SSRF_BLOCK_PRIVATE_IPS ergaenzt. JWT_REFRESH_EXPIRES_IN stand bereits in docker/.env.example, wurde aber nie durchgereicht - dieselbe Fehlerklasse wie beim Audit-Siegel. - Caddyfile: gzip fuer /api/* deaktiviert (BREACH, CVE-2013-3587). Die Konfiguration komprimierte bisher alles, obwohl die README das fuer die API ausdruecklich ausschliesst. Statische Assets bleiben komprimiert. Mit `caddy validate` geprueft. - docker/.env.example um die neuen Variablen erweitert, dazu ein Hinweis auf Sonderzeichen im DB-Passwort (dieser Stack setzt die DATABASE_URL direkt zusammen, anders als der Entrypoint im Projektstamm). - README benennt jetzt, wann welcher Stack der richtige ist. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -839,15 +839,24 @@ docker-compose logs -f app
|
|||||||
- **App**: Backend + Frontend in einem Container
|
- **App**: Backend + Frontend in einem Container
|
||||||
- **Caddy**: Reverse-Proxy mit automatischem SSL
|
- **Caddy**: Reverse-Proxy mit automatischem SSL
|
||||||
|
|
||||||
**Umgebungsvariablen (`docker/.env`):**
|
> **Wann dieser Stack?** Wenn du **keinen** eigenen Reverse-Proxy betreibst –
|
||||||
|
> Caddy holt und erneuert das SSL-Zertifikat hier selbst. Hast du bereits einen
|
||||||
|
> Proxy davor (nginx, Nginx Proxy Manager, Traefik), nimm den Stack im
|
||||||
|
> Projektstamm; er bringt bewusst keinen eigenen Proxy mit.
|
||||||
|
|
||||||
|
**Umgebungsvariablen (`docker/.env`):** – vollständige Vorlage mit Erklärungen
|
||||||
|
in `docker/.env.example`
|
||||||
|
|
||||||
```env
|
```env
|
||||||
MYSQL_ROOT_PASSWORD=sicheres-root-passwort
|
DB_ROOT_PASSWORD=sicheres-root-passwort
|
||||||
MYSQL_DATABASE=opencrm
|
DB_NAME=opencrm
|
||||||
MYSQL_USER=opencrm
|
DB_USER=opencrm
|
||||||
MYSQL_PASSWORD=sicheres-passwort
|
DB_PASSWORD=sicheres-passwort
|
||||||
JWT_SECRET=sehr-langer-zufaelliger-string
|
JWT_SECRET=sehr-langer-zufaelliger-string
|
||||||
ENCRYPTION_KEY=64-zeichen-hex-string
|
ENCRYPTION_KEY=64-zeichen-hex-string
|
||||||
DOMAIN=crm.example.com
|
DOMAIN=crm.example.com
|
||||||
|
HTTPS_ENABLED=true # Caddy terminiert TLS → Secure-Cookies an
|
||||||
|
AUDIT_HMAC_KEY= # Siegel fürs Audit-Log, siehe oben
|
||||||
RUN_SEED=true # Nur beim ersten Start
|
RUN_SEED=true # Nur beim ersten Start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,11 @@
|
|||||||
DOMAIN=localhost
|
DOMAIN=localhost
|
||||||
|
|
||||||
# Database
|
# Database
|
||||||
|
# HINWEIS: In diesem Stack wird die Verbindungs-URL direkt aus diesen Werten
|
||||||
|
# zusammengesetzt. Sonderzeichen im Passwort ($ ! # @ : / ?) koennen die URL
|
||||||
|
# zerlegen und den Start verhindern. Nutze hier ein langes Passwort aus
|
||||||
|
# Buchstaben und Ziffern - oder den Stack im Projektstamm, dessen Entrypoint
|
||||||
|
# die Zeichen korrekt kodiert.
|
||||||
DB_ROOT_PASSWORD=change-this-root-password
|
DB_ROOT_PASSWORD=change-this-root-password
|
||||||
DB_NAME=opencrm
|
DB_NAME=opencrm
|
||||||
DB_USER=opencrm
|
DB_USER=opencrm
|
||||||
@@ -24,6 +29,22 @@ JWT_REFRESH_EXPIRES_IN=7d
|
|||||||
# Generate with: openssl rand -hex 32
|
# Generate with: openssl rand -hex 32
|
||||||
ENCRYPTION_KEY=change-this-to-a-32-byte-hex-key
|
ENCRYPTION_KEY=change-this-to-a-32-byte-hex-key
|
||||||
|
|
||||||
|
# TLS: In diesem Stack terminiert Caddy das HTTPS. Das Flag setzt das
|
||||||
|
# Secure-Attribut am Refresh-Cookie, trust proxy = 1 Hop und
|
||||||
|
# upgrade-insecure-requests in der CSP.
|
||||||
|
# Nur auf false setzen, wenn der Stack bewusst ohne TLS laeuft (reiner Test).
|
||||||
|
HTTPS_ENABLED=true
|
||||||
|
|
||||||
|
# Nur noetig, wenn das Frontend unter einer ANDEREN Domain laeuft als das
|
||||||
|
# Backend. Beim Standard-Setup (alles hinter derselben Caddy-Domain) leer
|
||||||
|
# lassen.
|
||||||
|
CORS_ORIGINS=
|
||||||
|
|
||||||
|
# Cloud-Deploy: blockt zusaetzlich private IP-Ranges, damit ein eingeloggter
|
||||||
|
# Admin ueber die Provider-/SMTP-Testverbindung keine internen Dienste
|
||||||
|
# anpingen kann. On-Prem meist false, weil Mailserver oft lokal laufen.
|
||||||
|
SSRF_BLOCK_PRIVATE_IPS=false
|
||||||
|
|
||||||
# First Install: Set to "true" to seed database on first startup
|
# First Install: Set to "true" to seed database on first startup
|
||||||
# After first successful start, set back to "false"
|
# After first successful start, set back to "false"
|
||||||
RUN_SEED=true
|
RUN_SEED=true
|
||||||
|
|||||||
+7
-2
@@ -24,8 +24,13 @@
|
|||||||
Referrer-Policy "strict-origin-when-cross-origin"
|
Referrer-Policy "strict-origin-when-cross-origin"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Gzip compression
|
# Gzip compression – NUR fuer statische Assets, NICHT fuer /api/*.
|
||||||
encode gzip
|
# Komprimierte API-Antworten ermoeglichen die BREACH-Attacke
|
||||||
|
# (CVE-2013-3587): aus der Antwortgroesse liessen sich unter bestimmten
|
||||||
|
# Bedingungen Geheimnisse aus JSON-Responses ableiten. Statische Dateien
|
||||||
|
# sind davon nicht betroffen und bleiben komprimiert.
|
||||||
|
@nicht_api not path /api/*
|
||||||
|
encode @nicht_api gzip
|
||||||
|
|
||||||
# Handle file uploads (increase body limit)
|
# Handle file uploads (increase body limit)
|
||||||
request_body {
|
request_body {
|
||||||
|
|||||||
@@ -38,8 +38,24 @@ services:
|
|||||||
PORT: 3001
|
PORT: 3001
|
||||||
DATABASE_URL: mysql://${DB_USER:-opencrm}:${DB_PASSWORD:-opencrm123}@db:3306/${DB_NAME:-opencrm}
|
DATABASE_URL: mysql://${DB_USER:-opencrm}:${DB_PASSWORD:-opencrm123}@db:3306/${DB_NAME:-opencrm}
|
||||||
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required}
|
JWT_SECRET: ${JWT_SECRET:?JWT_SECRET is required}
|
||||||
JWT_EXPIRES_IN: ${JWT_EXPIRES_IN:-7d}
|
# Access-Token kurzlebig (lebt nur im Browser-Speicher → XSS-Fenster
|
||||||
|
# klein halten), Refresh-Token lang im httpOnly-Cookie.
|
||||||
|
# Frueher stand hier 7d fuer den ACCESS-Token – das war ein Ueberbleibsel
|
||||||
|
# aus der Zeit vor dem Access-/Refresh-Pattern.
|
||||||
|
JWT_EXPIRES_IN: ${JWT_EXPIRES_IN:-15m}
|
||||||
|
JWT_REFRESH_EXPIRES_IN: ${JWT_REFRESH_EXPIRES_IN:-7d}
|
||||||
ENCRYPTION_KEY: ${ENCRYPTION_KEY:?ENCRYPTION_KEY is required}
|
ENCRYPTION_KEY: ${ENCRYPTION_KEY:?ENCRYPTION_KEY is required}
|
||||||
|
# Caddy terminiert TLS und steht als EIN Proxy davor. Das Flag setzt das
|
||||||
|
# Secure-Attribut am Refresh-Cookie, trust proxy = 1 Hop und
|
||||||
|
# upgrade-insecure-requests in der CSP. Nur auf false setzen, wenn der
|
||||||
|
# Stack bewusst ohne TLS laeuft (reiner Test ueber http://).
|
||||||
|
HTTPS_ENABLED: ${HTTPS_ENABLED:-true}
|
||||||
|
# Nur noetig, wenn das Frontend unter einer anderen Origin laeuft.
|
||||||
|
CORS_ORIGINS: ${CORS_ORIGINS:-}
|
||||||
|
# In der Container-Umgebung muss der Dienst fuer Caddy erreichbar sein.
|
||||||
|
LISTEN_ADDR: ${LISTEN_ADDR:-0.0.0.0}
|
||||||
|
# Cloud-Deploy: private IP-Ranges fuer Provider-/SMTP-Tests blocken.
|
||||||
|
SSRF_BLOCK_PRIVATE_IPS: ${SSRF_BLOCK_PRIVATE_IPS:-false}
|
||||||
# Siegel für das Audit-Log (optional, fail-safe ohne Wert)
|
# Siegel für das Audit-Log (optional, fail-safe ohne Wert)
|
||||||
AUDIT_HMAC_KEY: ${AUDIT_HMAC_KEY:-}
|
AUDIT_HMAC_KEY: ${AUDIT_HMAC_KEY:-}
|
||||||
AUDIT_HMAC_KEY_OLD: ${AUDIT_HMAC_KEY_OLD:-}
|
AUDIT_HMAC_KEY_OLD: ${AUDIT_HMAC_KEY_OLD:-}
|
||||||
|
|||||||
Reference in New Issue
Block a user