fix: Rate-Limit hinter Reverse Proxy wirkte wie ein globales statt Pro-IP-Limit

RemoteIpAddress zeigte hinter Dokploy/Traefik ohne ForwardedHeaders-Middleware
für jede Anfrage auf dieselbe interne Proxy-IP - das Pro-IP-Limit (120/Minute)
wurde dadurch faktisch zu einem einzigen globalen Limit für die gesamte
Bereitstellung, geteilt von allen Geräten und Endpunkten zusammen.

ForwardedHeadersOptions (X-Forwarded-For/X-Forwarded-Proto) registriert,
KnownIPNetworks/KnownProxies geleert (Container nur über den Reverse Proxy
erreichbar). API-only-Fix, Server-Redeploy nötig.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 22:09:59 +02:00
co-authored by Claude Sonnet 5
parent 722b7f87ff
commit dea7da8cca
2 changed files with 38 additions and 0 deletions
+23
View File
@@ -1574,6 +1574,29 @@ die Docker-Verifikation unter 10.2.4 (kein Docker im Entwicklungsstand verfügba
(Brute-Force-Schutz), alle Endpunkte zusätzlich global auf 120 Anfragen/Minute je IP. Kestrel
`MaxRequestBodySize` auf 15 MB gedeckelt (Anhänge sind clientseitig ohnehin auf 10 MB
begrenzt, siehe `IAttachmentStorage.MaxSizeBytes`).
**Nachtrag (Bugfix — Pro-IP-Limit wirkte wie ein globales Limit für alle Geräte zusammen):**
Nutzer-Bug-Report — der zweite Gerätesync scheiterte wiederholt mit HTTP 429 (Too Many
Requests), obwohl der Nutzer den Endpunkt nach eigener Aussage nicht annähernd 120-mal pro
Minute manuell aufgerufen hatte. Ursache: das Pro-IP-Limit partitioniert nach
`HttpContext.Connection.RemoteIpAddress` — hinter dem Dokploy/Traefik-Reverse-Proxy ist das
für JEDE Anfrage dieselbe interne Proxy-Adresse, nicht die echte Client-IP, ohne
`ForwardedHeaders`-Middleware. Aus "120 Anfragen/Minute je Gerät" wurde dadurch faktisch "120
Anfragen/Minute für die gesamte Bereitstellung" — Auto-Sync (alle 5 Min.) auf beiden Geräten
plus manuelle Syncs plus die vorangegangenen Pairing-/Verbindungstests derselben Sitzung
teilten sich alle denselben Zähler und erschöpften ihn gemeinsam.
Behoben: `ForwardedHeadersOptions` (`XForwardedFor`/`XForwardedProto`) registriert und
`app.UseForwardedHeaders()` ganz am Anfang der Middleware-Pipeline eingehängt, `KnownIPNetworks`/
`KnownProxies` bewusst geleert — der Container ist nur über den Reverse Proxy erreichbar, der
unmittelbare Absender von `X-Forwarded-For` ist hier also vertrauenswürdig.
**Bekannte Einschränkung/Risiko:** `docker-compose.yml` published Port 5000 direkt
(`ports: ["5000:5000"]`). Ist dieser Port vom Internet aus erreichbar (abhängig von der
Docker-/Host-Firewall, nicht aus dem Compose-File allein ersichtlich), könnte ein Angreifer
Kestrel unter Umgehung von Traefik direkt ansprechen und `X-Forwarded-For` frei gefälscht
mitschicken, um das Rate-Limit vollständig zu umgehen. Nicht behoben, da die tatsächliche
Netzwerktopologie (Firewall-Regeln des Hosts) von hier aus nicht einsehbar ist — sollte vor
Ort geprüft werden.
- [x] **10.2.3** Serverseitiges Backup der Event-/Snapshot-Dateien.
**Umsetzung:** `docker/backup.sh` — Tar-Archiv von `./data` (Ereignis-Logs, Snapshots,