REVERSE_PROXY.md auf aktuelle Entitäts-/Dienstnamen korrigiert
Die Pfad-Freigabeliste ging noch von pyscript.audi_dashboard_*/ pyscript.reifen_* und /api/services/pyscript/... aus - seit der Umstellung auf die native Integration (2026-08-23) heißen Entitäten sensor.audi_dashboard_* und Dienste audi_dashboard.<name>. Ergänzt um zwei seither neu hinzugekommene Bedarfe, die in der alten Fassung fehlten: homeassistant.restart (Update-Neustart-Knopf) und /local/dm360/* (die ausgelieferte App selbst - ohne diesen Pfad hätte sie über den Tunnel gar nicht geladen). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,50 +1,67 @@
|
||||
# Reverse-Proxy: Pfad-Freigabeliste für DataMetric360
|
||||
|
||||
**Vorbereitung, noch nicht in Betrieb.** Gehört zu Phase 12 des
|
||||
`../UMSETZUNGSPLAN.md`; die Entscheidung dahinter steht in
|
||||
`../COMPANION_APP_ARCHITECTURE.md` §4.
|
||||
**Vorbereitet, noch nicht in Betrieb.** Gehört zu `../COMPANION_APP_ARCHITECTURE.md` §4. Reverse Proxy
|
||||
ist entschieden: **Nginx Proxy Manager** (Add-on), auf demselben Host wie Home Assistant selbst. Die
|
||||
konkreten Einrichtungsschritte (Cloudflare-Konto, Nameserver-Umstellung, Add-ons, ...) stehen in
|
||||
[`INTERNET_ZUGRIFF_EINRICHTEN.md`](INTERNET_ZUGRIFF_EINRICHTEN.md) - dieses Dokument ist nur die
|
||||
technische Pfad-Freigabeliste selbst, die dort im letzten Schritt eingetragen wird.
|
||||
|
||||
Ziel: Die App erreicht Home Assistant von unterwegs, **ohne dass Home Assistant
|
||||
selbst im Internet steht**. Dazu ein Cloudflare-Tunnel (baut nur nach außen auf,
|
||||
kein Port am Router) auf einen Reverse Proxy, der ausschließlich die unten
|
||||
aufgeführten Pfade durchlässt.
|
||||
Ziel: Die App erreicht Home Assistant von unterwegs, **ohne dass Home Assistant selbst im Internet
|
||||
steht**. Dazu ein Cloudflare-Tunnel (baut nur nach außen auf, kein Port am Router) auf einen Reverse
|
||||
Proxy, der ausschließlich die unten aufgeführten Pfade durchlässt.
|
||||
|
||||
> **2026-08-28 aktualisiert:** die ursprüngliche Fassung dieses Dokuments ging noch von den
|
||||
> pyscript-Zeiten aus (`pyscript.audi_dashboard_*`, `pyscript.reifen_*`, `/api/services/pyscript/...`).
|
||||
> Seit der Umstellung auf eine native Integration (2026-08-23, siehe `AGENTS.md` Abschnitt H) heißen
|
||||
> Entitäten `sensor.audi_dashboard_*` und Dienste `audi_dashboard.<name>` - keine doppelten Präfixe
|
||||
> mehr. Die Regeln unten sind auf den aktuellen Stand korrigiert und um zwei seither neu hinzugekommene
|
||||
> Bedarfe ergänzt (`homeassistant.restart` fürs Selbst-Update, `/local/dm360/*` für die App selbst -
|
||||
> beide fehlten in der alten Fassung ersatzlos, ohne sie hätte weder der Update-Neustart-Knopf noch die
|
||||
> ausgelieferte App über den Tunnel funktioniert).
|
||||
|
||||
## Was die App wirklich braucht
|
||||
|
||||
Aus dem tatsächlichen Code der Datenschicht (`companion-app/src/api/`)
|
||||
abgeleitet, nicht geschätzt:
|
||||
Aus dem tatsächlichen Code der Datenschicht (`companion-app/src/api/rest.ts`, `index.ts`) abgeleitet,
|
||||
nicht geschätzt - dort gibt es genau drei REST-Aufrufmuster plus WebSocket:
|
||||
|
||||
| Methode | Pfad | Wofür |
|
||||
|---|---|---|
|
||||
| `GET` | `/api/` | Verbindungsprüfung bei der Ersteinrichtung |
|
||||
| `GET` | `/api/states/pyscript.audi_dashboard_*` | Profil, Fahrten, Tankvorgänge, Status, Batterieverlauf, Belegergebnis, Updatestatus |
|
||||
| `GET` | `/api/states/pyscript.reifen_*` | Reifensatz-Kilometerstände |
|
||||
| `POST` | `/api/services/pyscript/audi_dashboard_*` | alle schreibenden Vorgänge |
|
||||
| `GET` | `/api/states/sensor.audi_dashboard_*` | Profil, Fahrten, Tankvorgänge, Status, Batterieverlauf, Belegergebnis, Importstatus, App-Version, Reifen-Kilometerstände |
|
||||
| `POST` | `/api/services/audi_dashboard/*` | alle schreibenden Vorgänge (Fahrt/Tankvorgang anlegen/ändern, CSV-/Historien-Import, Update prüfen/installieren, ...) |
|
||||
| `POST` | `/api/services/homeassistant/restart` | genau ein Dienst außerhalb der eigenen Domain - der "Jetzt neu starten"-Knopf nach einem Integrations-Update (siehe `AGENTS.md` Abschnitt L) |
|
||||
| `GET` (Upgrade) | `/api/websocket` | Live-Aktualisierung |
|
||||
| `GET` | `/local/dm360/*` | die ausgelieferte App selbst (`companion-app`, `vite.config.ts`: `base: "./"`, Ziel `/config/www/dm360/`) - ohne diesen Pfad lädt die Seite gar nicht erst |
|
||||
| `GET` | `/local/bilder/*.{webp,png,jpg,svg}` | Fahrzeugfotos, die die App anzeigt |
|
||||
|
||||
Alles andere wird geblockt, insbesondere `/auth/*`, `/lovelace*`, `/config*`,
|
||||
`/api/config`, `/api/history*`, `/developer-tools*` und die Oberfläche selbst.
|
||||
Alles andere wird geblockt, insbesondere `/auth/*`, `/lovelace*`, `/config*`, `/api/config`,
|
||||
`/api/history*`, `/developer-tools*` und die Home-Assistant-Oberfläche selbst.
|
||||
|
||||
## Ehrliche Einschränkung
|
||||
|
||||
**Der WebSocket lässt sich nicht pfadgenau beschneiden.** Nach `auth_ok` kann
|
||||
über `/api/websocket` grundsätzlich jeder Zustand gelesen werden, nicht nur die
|
||||
`pyscript.*`-Entitäten. Die Freigabeliste ist an dieser Stelle also grobkörniger
|
||||
als beim REST-Zugriff.
|
||||
**Der WebSocket lässt sich nicht pfadgenau beschneiden.** Nach `auth_ok` kann über `/api/websocket`
|
||||
grundsätzlich jeder Zustand gelesen werden, nicht nur die `audi_dashboard`-Entitäten. Die Freigabeliste
|
||||
ist an dieser Stelle also grobkörniger als beim REST-Zugriff.
|
||||
|
||||
Was bleibt: Ohne gültigen Token kommt gar keine Verbindung zustande, und ein
|
||||
verlorenes Gerät wird durch Zurückziehen genau seines Tokens ausgesperrt. Das
|
||||
ist die im Architekturdokument bewusst akzeptierte Abwägung — sie sollte nur
|
||||
nicht in Vergessenheit geraten.
|
||||
Was bleibt: Ohne gültigen Token kommt gar keine Verbindung zustande, und ein verlorenes Gerät wird durch
|
||||
Zurückziehen genau seines Tokens ausgesperrt. Das ist die im Architekturdokument bewusst akzeptierte
|
||||
Abwägung - sie sollte nur nicht in Vergessenheit geraten.
|
||||
|
||||
Wer sie nicht eingehen will, hat eine Alternative: den WebSocket weglassen und
|
||||
die App auf regelmäßiges Abfragen umstellen. Kostet Akku und Datenvolumen,
|
||||
verkleinert die Angriffsfläche aber auf die exakt aufgeführten REST-Pfade.
|
||||
Wer sie nicht eingehen will, hat eine Alternative: den WebSocket weglassen und die App auf regelmäßiges
|
||||
Abfragen umstellen. Kostet Akku und Datenvolumen, verkleinert die Angriffsfläche aber auf die exakt
|
||||
aufgeführten REST-Pfade.
|
||||
|
||||
`homeassistant.restart` verdient eine eigene Erwähnung: wer den Token besitzt, kann damit die gesamte
|
||||
Home-Assistant-Instanz neu starten, nicht nur etwas im Rahmen dieser App. Dasselbe gilt lokal schon
|
||||
heute (derselbe Token, dieselbe Berechtigung) - der Tunnel ändert am Berechtigungsumfang nichts, nur am
|
||||
Netzwerkpfad. Wer das nicht mit ausliefern will: den Block für `homeassistant/restart` unten weglassen -
|
||||
der Update-Knopf zeigt dann nach einem Update lediglich "bitte manuell neu starten" statt selbst zu
|
||||
handeln (kein Absturz, siehe `AGENTS.md` Abschnitt L).
|
||||
|
||||
## Nginx Proxy Manager
|
||||
|
||||
Im Add-on unter *Hosts → Proxy Hosts → Edit → Advanced* eintragen. Ziel ist der
|
||||
interne Name der Home-Assistant-Instanz (im Supervisor-Netz `homeassistant:8123`).
|
||||
Im Add-on unter *Hosts → Proxy Hosts → Edit → Advanced* eintragen. Ziel ist der interne Name der
|
||||
Home-Assistant-Instanz (im Supervisor-Netz `homeassistant:8123`).
|
||||
|
||||
```nginx
|
||||
# Reihenfolge zählt: die erlaubenden Blöcke stehen vor dem pauschalen Verbot.
|
||||
@@ -54,13 +71,22 @@ location = /api/ {
|
||||
include conf.d/include/proxy.conf;
|
||||
}
|
||||
|
||||
location ~ ^/api/states/pyscript\.(audi_dashboard_[a-z_]+|reifen_[a-z_]+)$ {
|
||||
location ~ ^/api/states/sensor\.audi_dashboard_[a-z0-9_]+$ {
|
||||
limit_except GET { deny all; }
|
||||
proxy_pass http://homeassistant:8123;
|
||||
include conf.d/include/proxy.conf;
|
||||
}
|
||||
|
||||
location ~ ^/api/services/pyscript/audi_dashboard_[a-z_]+$ {
|
||||
location ~ ^/api/services/audi_dashboard/[a-z_]+$ {
|
||||
limit_except POST { deny all; }
|
||||
proxy_pass http://homeassistant:8123;
|
||||
include conf.d/include/proxy.conf;
|
||||
}
|
||||
|
||||
# Genau ein Dienst außerhalb der eigenen Domain - siehe "Ehrliche
|
||||
# Einschränkung" oben, wer ihn nicht mit ausliefern will, lässt diesen
|
||||
# Block weg.
|
||||
location = /api/services/homeassistant/restart {
|
||||
limit_except POST { deny all; }
|
||||
proxy_pass http://homeassistant:8123;
|
||||
include conf.d/include/proxy.conf;
|
||||
@@ -75,6 +101,13 @@ location = /api/websocket {
|
||||
include conf.d/include/proxy.conf;
|
||||
}
|
||||
|
||||
# Die App selbst (companion-app, ausgeliefert unter /config/www/dm360/).
|
||||
location ^~ /local/dm360/ {
|
||||
limit_except GET { deny all; }
|
||||
proxy_pass http://homeassistant:8123;
|
||||
include conf.d/include/proxy.conf;
|
||||
}
|
||||
|
||||
# Die Fotos der Fahrzeuge, die die App anzeigt. Nur Lesen, nur Bilder.
|
||||
location ~ ^/local/bilder/[a-z0-9_-]+\.(webp|png|jpg|svg)$ {
|
||||
limit_except GET { deny all; }
|
||||
@@ -88,14 +121,16 @@ location / {
|
||||
}
|
||||
```
|
||||
|
||||
Wird die App selbst unter derselben Adresse ausgeliefert, braucht sie einen
|
||||
eigenen Block auf ihr Verzeichnis — sauberer ist ein getrennter Hostname
|
||||
(`datametric360.app` für die App, `api.datametric360.app` für die
|
||||
Schnittstelle), dann bleibt diese Liste unverändert.
|
||||
Wird die App selbst unter derselben Adresse ausgeliefert (`/local/dm360/` auf demselben Hostnamen wie
|
||||
die Schnittstelle), braucht dieser Proxy-Host nur die Blöcke oben - kein separater Host nötig. Ein
|
||||
getrennter Hostname für die Schnittstelle (`api.datametric360.app` statt `datametric360.app`) ist
|
||||
trotzdem sauberer, siehe `COMPANION_APP_ARCHITECTURE.md` §5 Punkt 4: dann bleibt der `/local/dm360/`-Block
|
||||
auf dem App-Host, die übrigen Blöcke auf dem API-Host.
|
||||
|
||||
## Nach der Einrichtung prüfen
|
||||
|
||||
Von einem Netz ohne VPN, etwa über Mobilfunk:
|
||||
Von einem Netz ohne VPN, etwa über Mobilfunk (Hostnamen unten sind Platzhalter - eintragen, was bei der
|
||||
Einrichtung tatsächlich gewählt wurde):
|
||||
|
||||
```bash
|
||||
API=https://api.datametric360.app
|
||||
@@ -104,7 +139,7 @@ T=<Zugriffstoken>
|
||||
# muss gehen
|
||||
curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $T" "$API/api/"
|
||||
curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $T" \
|
||||
"$API/api/states/pyscript.audi_dashboard_profil"
|
||||
"$API/api/states/sensor.audi_dashboard_profil"
|
||||
|
||||
# muss 404 oder 403 liefern
|
||||
curl -s -o /dev/null -w '%{http_code}\n' "$API/auth/authorize"
|
||||
@@ -113,14 +148,18 @@ curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $T" \
|
||||
"$API/api/states/sensor.audi_rs_4_avant_mileage"
|
||||
|
||||
# ohne Token: 401, nicht 200
|
||||
curl -s -o /dev/null -w '%{http_code}\n' "$API/api/states/pyscript.audi_dashboard_profil"
|
||||
curl -s -o /dev/null -w '%{http_code}\n' "$API/api/states/sensor.audi_dashboard_profil"
|
||||
```
|
||||
|
||||
Zusätzlich: Port 8123 darf von außen **gar nicht** antworten.
|
||||
|
||||
## Offen bis zur Einrichtung
|
||||
## Noch offen
|
||||
|
||||
- Nameserver von `datametric360.app` auf Cloudflare umstellen („Full setup")
|
||||
- Entscheidung Nginx Proxy Manager gegen Traefik (die Liste oben ist für NPM
|
||||
geschrieben und für Traefik sinngemäß zu übertragen)
|
||||
- Endgültige Hostnamen festlegen und hier eintragen
|
||||
Siehe [`INTERNET_ZUGRIFF_EINRICHTEN.md`](INTERNET_ZUGRIFF_EINRICHTEN.md) für den vollständigen,
|
||||
geordneten Ablauf - kurz zusammengefasst bleibt vor der ersten echten Nutzung dieser Liste offen:
|
||||
|
||||
- Cloudflare-Konto anlegen, `datametric360.app`-Zone hinzufügen
|
||||
- Nameserver von `datametric360.app` bei all-inkl auf Cloudflare umstellen („Full setup")
|
||||
- Cloudflared- und Nginx-Proxy-Manager-Add-ons installieren, Tunnel + öffentliche Hostnamen einrichten
|
||||
- Endgültige Hostnamen festlegen (App-Domain vs. API-Subdomain) und hier eintragen
|
||||
- companion-app-Web-Build nach `/config/www/dm360/` ausliefern
|
||||
|
||||
Reference in New Issue
Block a user