4.9 KiB
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=<IP des Docker-Hosts oder *>
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
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/<token> bedeutet es, dass der Token nur an die eigene Herkunft übertragen
würde. Wer es strenger mag, stellt die OPNsense auf no-referrer um.