Files
MindSift/Doku/reverse-proxy.md
T
sandrodachandClaude Opus 5.5 f57a9884d1
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m32s
fix(security): stream tickets instead of session tokens in URLs (FLAW-22)
WebSocket and EventSource put the 30-day session token into the URL,
where proxies log it. The app now gets a one-time ticket right before
connecting (POST /auth/stream-ticket, 60 s, bound to the token version);
?token= is no longer accepted for streams, non-browser clients may use
the Authorization header.

With BASE_URL set, CORS allows only that origin and WebSocket upgrades
only it or the requested host; credentials are off (no cookies exist).
Logout, ending all sessions, a new password and deactivation close all
live streams of the account; clients with a valid session reconnect
with a fresh ticket.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 14:46:58 +01:00

250 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 🛡️ Reverse-Proxy & WebServer-Konfigurationshandbuch
Dieses Dokument beschreibt die empfohlene Konfiguration von Reverse-Proxies und Ingress-Controllern für MindSift. MindSift nutzt eine **hybride Realtime-Architektur** bestehend aus bidirektionalen **WebSockets** und einem automatischen **Server-Sent Events (SSE) Fallback**, um selbst in restriktivsten Firmennetzwerken, hinter strikten Firewalls oder bei Deep-Packet-Inspection unterbrechungsfreie Live-Synchronisation zu gewährleisten.
---
## ⚡ Wichtige Anforderungen auf einen Blick
| Protokoll | Standard-Port | Benötigte Header & Einstellungen | Zweck |
| :--- | :--- | :--- | :--- |
| **WebSockets** | 443 (WSS) / 80 (WS) | `Upgrade $http_upgrade`<br>`Connection "Upgrade"`<br>`proxy_read_timeout 3600s` | Bidirektionale Live-Updates (Aufgaben, Listen, Notizen) |
| **Server-Sent Events (SSE)** | 443 (HTTPS) / 80 (HTTP) | `X-Accel-Buffering: no`<br>`proxy_buffering off`<br>`proxy_cache off` | Fallback bei blockiertem WebSocket-Upgrade |
| **REST API & Uploads** | 443 (HTTPS) / 80 (HTTP) | `client_max_body_size 64M` | Notizen, Import/Export (JSON) |
| **Client-IP** | – | `X-Real-IP` / `X-Forwarded-For` | Login-Sperre nach Fehlversuchen trifft den richtigen Absender |
### Client-IP und `TRUSTED_PROXIES`
MindSift sperrt Anmeldungen nach Fehlversuchen auch pro IP-Adresse. Hinter einem Proxy kommt jede Verbindung vom Proxy; die echte Adresse steht in `X-Forwarded-For` bzw. `X-Real-IP`. MindSift glaubt diesen Kopfzeilen nur, wenn die Verbindung von einem vertrauenswürdigen Proxy kommt, sonst könnte jeder, der den Port direkt erreicht, eine beliebige Adresse vortäuschen.
| `TRUSTED_PROXIES` | Wirkung |
|---|---|
| `private` (Standard) | Proxys aus localhost, Docker- und Heimnetzen (`10.x`, `172.16–31.x`, `192.168.x`, `fd…`, `100.64.x`). Passt für Proxy im selben Docker-Netz oder auf demselben Host – nichts einzustellen. |
| `private,198.51.100.0/24` | zusätzlich eigene Bereiche, z. B. ein externer Proxy-Server oder die [IP-Bereiche von Cloudflare](https://www.cloudflare.com/ips/) |
| `all` | Kopfzeilen immer glauben (früheres Verhalten) – Notausgang, wenn die Erkennung Probleme macht |
| `none` | Kopfzeilen nie glauben |
Schickt ein vertrauenswürdiger Proxy gar keine Client-IP mit, steht einmal eine Warnung im Log, und für diese Anfragen gilt nur noch die Sperre pro Konto. Die ganze Login-Sperre lässt sich mit `LOGIN_PROTECTION=false` abschalten.
---
## 1. Nginx
Eine typische `nginx.conf` bzw. Site-Konfiguration unter `/etc/nginx/sites-available/mindsift`:
```nginx
# Map für sauberes Connection-Handling bei WebSocket-Upgrades
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name mindsift.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name mindsift.example.com;
ssl_certificate /etc/letsencrypt/live/mindsift.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mindsift.example.com/privkey.pem;
# Maximale Payload-Größe für große Notizen und Exporte
client_max_body_size 64M;
# MindSift Frontend & REST API
location / {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# 1. WebSocket Endpunkte (/api/v1/ws)
location /api/v1/ws {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
# Upgrade-Header zwingend erforderlich
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Verhindert vorzeitigen Verbindungsabbruch durch Proxy-Inaktivitäts-Timeout
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
# 2. Server-Sent Events Fallback (/api/v1/events & /api/v1/sync/)
location ~ ^/api/v1/(events|sync/) {
proxy_pass http://127.0.0.1:8080;
proxy_http_version 1.1;
# Pufferung zwingend deaktivieren für sofortiges Event-Streaming
proxy_buffering off;
proxy_cache off;
proxy_set_header X-Accel-Buffering no;
proxy_set_header Connection '';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400s;
}
}
```
---
## 2. Traefik (Docker Compose / Ingress)
Bei Verwendung von Traefik Version 2/3 müssen keine expliziten WebSocket-Upgrades deklariert werden, da Traefik diese transparent durchleitet. Für Server-Sent Events und Timeouts empfehlen sich Middleware-Definitionen:
```yaml
version: '3.8'
services:
mindsift:
image: ghcr.io/sandrodach/mindsift:latest
restart: unless-stopped
environment:
- PORT=8080
- DB_PATH=/app/data/mindsift.db
volumes:
- ./data:/app/data
labels:
- "traefik.enable=true"
- "traefik.http.routers.mindsift.rule=Host(`mindsift.example.com`)"
- "traefik.http.routers.mindsift.entrypoints=websecure"
- "traefik.http.routers.mindsift.tls.certresolver=letsencrypt"
- "traefik.http.services.mindsift.loadbalancer.server.port=8080"
# Verhindert Pufferung und hält persistente SSE-Streams am Leben
- "traefik.http.middlewares.mindsift-buffering.buffering.maxRequestBodyBytes=67108864"
- "traefik.http.routers.mindsift.middlewares=mindsift-buffering"
```
---
## 3. Caddy Server (Caddyfile)
Caddy unterstützt WebSockets und HTTP/2 Server-Sent Events von Haus aus vollständig ("out-of-the-box"):
```caddyfile
mindsift.example.com {
encode gzip zstd
# Event-Stream (SSE) ohne Pufferung weiterleiten
reverse_proxy 127.0.0.1:8080 {
# Streaming für SSE optimieren
flush_interval -1
}
}
```
---
## 4. Cloudflare Proxy (Orange Cloud)
Wird MindSift hinter dem Cloudflare CDN betrieben:
1. **WebSockets aktivieren:**
- Navigiere in das Cloudflare Dashboard: **Network** > **WebSockets** > Schalter auf **ON** stellen.
2. **Cloudflare Rocket Loader / Auto Minify:**
- Deaktiviere "Rocket Loader" für MindSift, da asynchrone Skripteingriffe den Start der Single-Page-App stören können.
3. **Cloudflare WAF / Bot Fight Mode:**
- Stelle sicher, dass `GET /api/v1/events` und `GET /api/v1/ws*` nicht von "Challenge"-Seiten blockiert werden.
4. **Client-IP:** Cloudflare liefert die Besucheradresse in `CF-Connecting-IP`/`X-Forwarded-For`. Spricht Cloudflare MindSift direkt an (ohne eigenen Proxy dazwischen), die Cloudflare-Bereiche in `TRUSTED_PROXIES` ergänzen.
5. **SSE-Unterstützung:**
- Cloudflare unterstützt Server-Sent Events nativ über HTTP/2 und HTTP/3. Dank der vom MindSift-Backend gesetzten Header `X-Accel-Buffering: no` und `Cache-Control: no-cache` streamt Cloudflare Echtzeit-Events verzögerungsfrei an die Clients.
---
## 5. Apache 2.4 (`mod_proxy_wstunnel`)
Unter Debian/Ubuntu:
```bash
sudo a2enmod proxy proxy_http proxy_wstunnel headers
sudo systemctl restart apache2
```
VirtualHost-Konfiguration (`/etc/apache2/sites-available/mindsift.conf`):
```apache
<VirtualHost *:443>
ServerName mindsift.example.com
SSLEngine on
SSLCertificateFile /etc/letsencrypt/live/mindsift.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/mindsift.example.com/privkey.pem
# 1. WebSocket Tunneling
RewriteEngine On
RewriteCond %{HTTP:Upgrade} =websocket [NC]
RewriteRule /(.*) ws://127.0.0.1:8080/$1 [P,L]
RewriteCond %{HTTP:Upgrade} !=websocket [NC]
RewriteRule /(.*) http://127.0.0.1:8080/$1 [P,L]
# 2. SSE Response Flushing
<Location "/api/v1/events">
ProxyPass "http://127.0.0.1:8080/api/v1/events" flushpackets=on
ProxyPassReverse "http://127.0.0.1:8080/api/v1/events"
</Location>
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>
```
---
## Wenn der Proxy nicht angepasst werden kann
MindSift funktioniert auch hinter Proxys, die WebSocket blocken, Streams puffern oder nur kurze Anfragen erlauben. Die Notiz-Synchronisation wählt automatisch den besten verfügbaren Weg (WebSocket → SSE → Long-Polling → Abfrage alle 3 s); der Editor zeigt ihn an. Long-Polling-Anfragen dauern höchstens 25 s und bleiben damit unter üblichen Proxy-Grenzen. Details: [api/websocket.md](api/websocket.md).
## 🔍 Diagnose & Verifikation
Mit den folgenden Befehlen lässt sich die Erreichbarkeit und Streaming-Funktion prüfen:
### 1. Server-Sent Events Test (Standard-HTTPS)
```bash
curl -N -H "Accept: text/event-stream" -H "Authorization: Bearer <DEIN_TOKEN>" https://mindsift.example.com/api/v1/events
```
*Erwartete Ausgabe:*
```text
: connected (transport: sse, id: sse_...)
data: {"event":"SSE_CONNECTED","payload":{"sseId":"sse_...","transport":"sse"},"timestamp":"..."}
: ping
```
### 2. WebSocket Handshake Test
```bash
curl -i -N \
-H "Connection: Upgrade" \
-H "Upgrade: websocket" \
-H "Host: mindsift.example.com" \
-H "Origin: https://mindsift.example.com" \
-H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \
-H "Sec-WebSocket-Version: 13" \
-H "Authorization: Bearer <DEIN_TOKEN>" \
https://mindsift.example.com/api/v1/ws
```
*Erwartete Antwort:* `101 Switching Protocols`
---
## 🧭 Verwandte Dokumente
- 🔌 [WebSocket & Live-Protokoll](api/websocket.md)
- 🏗️ [Gesamtarchitektur](architecture.md)
- 🚨 [Bekannte Schwachstellen & Flaws](flaws.md)