Anmeldung: Host-Abweichung frueh erkennen, weniger auf der Anmeldeseite verraten
Im Betrieb aufgetreten: Die Anwendung lief hinter einem Reverse Proxy unter einer Domain, in der .env stand aber noch die interne Adresse aus der Einrichtung. Der Anmeldeknopf setzt den Cookie mit dem laufenden Vorgang auf dem Host, ueber den der Browser da ist — die Rueckkehradresse fuer den Anbieter kommt dagegen aus BASE_URL. Der Anbieter schickte den Browser also auf den anderen Host, der Cookie blieb zurueck, und am Ende stand „Der Anmeldevorgang ist abgelaufen". Eine Meldung, die die Ursache gut versteckt. Jetzt prueft der Start beide Adressen und bricht sofort mit einer Erklaerung ab. Auf dem Bildschirm steht nur die aufgerufene Adresse — die interne gehoert ins Protokoll, nicht auf eine Seite, die jeder sieht. Aus demselben Grund faellt der Hinweis „Die Anmeldung mit Passwort ist abgeschaltet (OIDC_ONLY=true)" weg. Wie die Anmeldung eingerichtet ist, geht Fremde nichts an; wer hierher gehoert, sieht den einen Knopf und weiss, was zu tun ist. Beim allerersten Aufruf bleibt der Hinweis, dass der Erste Administrator wird — der ist dann nuetzlich. Dazu die Anleitungen: Der Reverse-Proxy-Abschnitt im README nennt jetzt die drei Kopfzeilen, die die Anwendung vom Proxy erwartet, und was ohne sie passiert; dazu client_max_body_size fuer Uploads, ein Weg zum Nachpruefen und die Frage, fuer wen die Codes erreichbar sein sollen. In authentik.md steht, warum BASE_URL und die Adresse im Browser dieselbe sein muessen.
This commit is contained in:
@@ -124,9 +124,25 @@ BASE_URL=http://192.168.1.50:3000
|
||||
> **Warum `BASE_URL` so wichtig ist:** Ein dynamischer QR-Code enthält nicht
|
||||
> das eigentliche Ziel, sondern die Kurz-URL `{BASE_URL}/r/{slug}`. Steht dort
|
||||
> `localhost`, funktioniert der Code **nur auf dem Server selbst** — ein Handy
|
||||
> läuft ins Leere. Trage deshalb die LAN-Adresse des Servers oder die
|
||||
> öffentliche Domain ein. Bereits gedruckte Codes lassen sich später nicht
|
||||
> mehr umbiegen, also am besten vor dem ersten produktiven Code festlegen.
|
||||
> läuft ins Leere. Bereits gedruckte Codes lassen sich später nicht mehr
|
||||
> umbiegen, also am besten vor dem ersten produktiven Code festlegen.
|
||||
>
|
||||
> Die Frage dahinter lautet: **Wer soll die Codes scannen können?**
|
||||
>
|
||||
> | Wer scannt | Was in `BASE_URL` gehört |
|
||||
> | --- | --- |
|
||||
> | nur Leute im eigenen Netz oder per VPN | die interne Adresse, z. B. `http://192.168.1.50:3000` |
|
||||
> | auch Kundschaft, Besucher, Aushänge im Laden | eine **öffentlich auflösbare** Domain, z. B. `https://qr.example.com` |
|
||||
>
|
||||
> Eine Domain, die nur im internen DNS steht, verhält sich dabei wie eine
|
||||
> LAN-Adresse: Ohne VPN kommt niemand darauf. Ein gedruckter Code führt dann
|
||||
> für alle anderen ins Leere — mit „Server nicht gefunden“, ohne jeden
|
||||
> Hinweis darauf, woran es liegt.
|
||||
>
|
||||
> Und: `BASE_URL` muss **dieselbe** Adresse sein, über die ihr die Oberfläche
|
||||
> aufruft — nicht bloß irgendeine erreichbare. Sonst scheitert die Anmeldung
|
||||
> über einen externen Anbieter, siehe
|
||||
> [docs/authentik.md](docs/authentik.md#base_url-und-die-adresse-im-browser-müssen-dieselbe-sein).
|
||||
|
||||
Der Hostname `db` im `DATABASE_URL` bleibt unverändert — das ist der
|
||||
Servicename aus der `docker-compose.yml`, nicht dein Server.
|
||||
@@ -234,7 +250,7 @@ BASE_URL=https://qr.deine-domain.de
|
||||
| Scheme | `http` |
|
||||
| Forward Hostname / IP | IP des Docker-Hosts, z. B. `192.168.1.50` |
|
||||
| Forward Port | `3000` |
|
||||
| Websockets Support | an |
|
||||
| Websockets Support | nicht nötig — die Anwendung benutzt keine; angeschaltet schadet es aber nicht |
|
||||
| Block Common Exploits | an |
|
||||
| SSL | Zertifikat anfordern, *Force SSL* aktivieren |
|
||||
|
||||
@@ -249,9 +265,76 @@ Port. Danach:
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
> `BASE_URL` muss zur Adresse passen, unter der Besucher die Anwendung
|
||||
> aufrufen. Steht dort weiterhin `http://192.168.1.50:3000`, zeigen neue
|
||||
> dynamische Codes ins interne Netz statt auf die Domain.
|
||||
> `BASE_URL` muss **dieselbe** Adresse sein, unter der ihr die Anwendung
|
||||
> aufruft — nicht bloß irgendeine erreichbare. Steht dort weiterhin
|
||||
> `http://192.168.1.50:3000`, zeigen neue dynamische Codes ins interne Netz
|
||||
> statt auf die Domain, und die Anmeldung über einen externen Anbieter
|
||||
> scheitert (siehe unten).
|
||||
|
||||
### Was der Proxy weiterreichen muss
|
||||
|
||||
Die Anwendung wertet drei Kopfzeilen aus, die erst der Proxy setzt. Der Nginx
|
||||
Proxy Manager schickt alle drei von sich aus mit — bei anderen Proxys muss man
|
||||
sie eintragen.
|
||||
|
||||
| Kopfzeile | Wofür sie gebraucht wird | Fehlt sie, passiert das |
|
||||
| --- | --- | --- |
|
||||
| `X-Forwarded-Proto` | erkennt, dass die Verbindung über HTTPS läuft | Der Sitzungs-Cookie wird ohne `Secure` gesetzt und ginge auch über eine unverschlüsselte Verbindung mit |
|
||||
| `X-Forwarded-Host` | die aufgerufene Domain — für mehrere Domains und für die Anmeldung | Kurz-URLs unter Zweitdomains werden nicht zugeordnet |
|
||||
| `X-Forwarded-For` | Absender für die Bremse gegen Durchprobieren | Alle Aufrufe zählen als derselbe Absender |
|
||||
|
||||
Für ein reines nginx (ohne NPM) heißt das:
|
||||
|
||||
```nginx
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
```
|
||||
|
||||
Bei **Traefik** und **Caddy** ist nichts zu tun — beide setzen die Kopfzeilen
|
||||
von sich aus.
|
||||
|
||||
### Größe der Uploads
|
||||
|
||||
PDFs und Bilder gehen durch den Proxy. nginx bricht Anfragen oberhalb von
|
||||
`client_max_body_size` mit **413** ab, und zwar bevor die Anwendung sie zu
|
||||
sehen bekommt — die Fehlermeldung kommt dann vom Proxy und nicht aus der
|
||||
Oberfläche.
|
||||
|
||||
Der Wert muss also mindestens so groß sein wie `MAX_UPLOAD_MB` (Voreinstellung
|
||||
10 MB), besser etwas darüber. Im Nginx Proxy Manager steht das unter
|
||||
**Advanced → Custom Nginx Configuration**:
|
||||
|
||||
```nginx
|
||||
client_max_body_size 20m;
|
||||
```
|
||||
|
||||
### Nachprüfen, ob es stimmt
|
||||
|
||||
```bash
|
||||
# Kommt die Anwendung überhaupt an?
|
||||
curl -I https://qr.deine-domain.de/
|
||||
|
||||
# Kennt der Container dieselbe Adresse wie der Browser?
|
||||
docker compose exec app env | grep BASE_URL
|
||||
```
|
||||
|
||||
Beides muss zusammenpassen. Ein häufiger Fall: Der Proxy läuft, die Domain
|
||||
öffnet sich, aber `BASE_URL` steht noch auf der internen IP aus der
|
||||
Einrichtung. Dann ist die Oberfläche zwar erreichbar, aber
|
||||
|
||||
- jeder neue dynamische Code zeigt auf die interne Adresse, und
|
||||
- die Anmeldung über einen externen Anbieter endet mit einer Fehlermeldung,
|
||||
weil der Anbieter den Browser auf den anderen Host zurückschickt — siehe
|
||||
[docs/authentik.md](docs/authentik.md#base_url-und-die-adresse-im-browser-müssen-dieselbe-sein).
|
||||
|
||||
### Erreichbar für wen?
|
||||
|
||||
Der Proxy entscheidet mit, wer die Codes scannen kann. Steht die Domain nur im
|
||||
internen DNS oder ist sie nur über VPN erreichbar, funktionieren gedruckte
|
||||
Codes **auch nur dort**. Für Aushänge, Flyer oder Verpackungen muss die Domain
|
||||
öffentlich auflösbar und der Proxy von außen erreichbar sein.
|
||||
|
||||
### Mehrere Domains
|
||||
|
||||
|
||||
Reference in New Issue
Block a user