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:
2026-08-28 12:01:19 +02:00
parent 6b35de458d
commit bf1f9646e2
+80 -41
View File
@@ -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