# Betrieb hinter dem Reverse Proxy (OPNsense) Aufbau: Browser → HTTPS → nginx auf der OPNsense → HTTP → Port 46600 auf dem Docker-Host → nginx im `web`-Container → `api`. ## Drei Werte in der `.env` ``` PUBLIC_BASE_URL=https://einkauf.example.de COOKIE_SECURE=true FORWARDED_ALLOW_IPS= ``` Nach jeder Änderung `docker compose up -d`, nicht `restart` – ein Neustart übernimmt keine geänderten Umgebungswerte. **`PUBLIC_BASE_URL`** landet in jeder Verifikations-, Einladungs- und Passwort-Reset-Mail. Steht dort noch `http://…:46600`, führen die Links ins Leere, sobald jemand von außen klickt. **`COOKIE_SECURE=true`** ist ab jetzt Pflicht, aber auch erst ab jetzt möglich: Ein `Secure`-Cookie über reines HTTP wird vom Browser verworfen, und die Anmeldung scheitert dann ohne verwertbare Meldung. Beides muss gleichzeitig umgestellt werden. ## Was die OPNsense weiterreichen muss Im nginx-Plugin unter *Http(s) → Location* für den Upstream: ``` 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_set_header X-Forwarded-Host $host; ``` `X-Forwarded-Proto` ist der wichtigste Wert: Ohne ihn hält die Anwendung die Verbindung für unverschlüsselt. Zusätzlich für Server-Sent Events (ab Phase 4): ``` proxy_buffering off; proxy_read_timeout 3600s; ``` Ohne `proxy_buffering off` sammelt nginx die Ereignisse und gibt sie erst gebündelt weiter – die Liste aktualisiert sich dann mit Verzögerung oder gar nicht. ## Client-IP fürs Rate Limiting Mit zwei Proxys davor sieht die Anwendung ohne Weiterreichung nur die IP des `web`-Containers. Dann würde eine einzige IP das Limit für alle auslösen. `X-Forwarded-For` wird durch beide Stufen durchgereicht; uvicorn wertet es wegen `--proxy-headers` aus. `FORWARDED_ALLOW_IPS=*` ist vertretbar, solange der `api`-Container keinen veröffentlichten Port hat – dann kann den Header nur der `web`-Container setzen. Sobald du den API-Port für Debugging freigibst, gehört dort die konkrete IP hinein. ## PWA-Installation Erst über HTTPS bietet der Browser „Zur Startseite hinzufügen" an und lässt den Service Worker zu. Bei iOS ist die Installation zusätzlich Voraussetzung dafür, dass Web Push in Phase 8 überhaupt funktioniert – im normalen Safari-Tab gibt es keine Benachrichtigungen. ## Prüfen ```bash curl -sI https://einkauf.example.de/ | grep -i strict-transport curl -s https://einkauf.example.de/readyz ``` Und nach der Anmeldung im Browser unter *Entwicklerwerkzeuge → Anwendung → Cookies*: `ea_session` muss `Secure` und `HttpOnly` tragen, `ea_csrf` nur `Secure`. ## Kopfzeilen: was aus dem Protokoll hervorgeht Eine Auswertung der tatsächlich ausgelieferten Antworten hat drei Punkte ergeben, die auf der OPNsense-Seite liegen. ### `X-XSS-Protection: 1` entfernen Die OPNsense setzt diese Kopfzeile. Sie ist überholt: Chrome hat den zugehörigen Filter entfernt, und in den Browsern, die ihn noch kennen, kann er in Einzelfällen selbst Lücken aufreißen, indem er Teile der Seite unterdrückt. Der Wert `1` ohne `mode=block` ist dabei die ungünstigste Variante. Empfehlung: im nginx-Plugin abschalten oder auf `0` setzen. Der Schutz kommt von der Content-Security-Policy, nicht von dieser Kopfzeile. ### HSTS: `preload` passt nicht zur Laufzeit ``` strict-transport-security: max-age=15768000; includeSubDomains; preload ``` Das sind gut sechs Monate. Für die Aufnahme in die Preload-Liste verlangen die Browserhersteller mindestens ein Jahr (`31536000`); mit dem jetzigen Wert wird das `preload` schlicht ignoriert. Entweder auf `31536000` erhöhen – dann aber im Bewusstsein, dass ein Eintrag in der Preload-Liste die Domain für alle Unterdomains dauerhaft auf HTTPS festlegt und sich nur mit Monaten Vorlauf rückgängig machen lässt – oder das `preload` weglassen. ### Doppelte Kopfzeilen `X-Content-Type-Options`, `X-Frame-Options` und `Permissions-Policy` kommen jeweils zweimal an: einmal vom `web`-Container, einmal von der OPNsense. Das ist unschädlich, aber unnötig. Wichtiger ist, was daraus folgt: **Eine doppelte `Content-Security-Policy` wäre nicht unschädlich.** Browser wenden dann beide an und lassen nur zu, was in *beiden* erlaubt ist. Eine zusätzliche CSP auf der OPNsense könnte die Anwendung also lahmlegen, ohne dass eine Fehlermeldung darauf hinweist – sichtbar wäre nur, dass Module oder Stile nicht laden. Derzeit setzt die OPNsense keine, das sollte so bleiben. Die OPNsense überschreibt außerdem `Referrer-Policy` mit `same-origin` statt des strengeren `no-referrer` aus dem Container. Unkritisch, solange die Anwendung nicht nach außen verlinkt – für die öffentlichen Listenlinks unter `/s/` bedeutet es, dass der Token nur an die eigene Herkunft übertragen würde. Wer es strenger mag, stellt die OPNsense auf `no-referrer` um.