PWA laeuft: Manifest, Symbole in allen noetigen Groessen und die iOS-Angaben, die Apple statt des Manifests auswertet. Das Startsymbol ist bewusst neutral - die Vier Ringe auf einem Homescreen waeren nach aussen sichtbar und fielen nicht mehr unter die private Nutzung. Vorbereitet, aber nicht ausgefuehrt: Capacitor-Konfiguration, ein Ablage-Adapter fuer Schluesselbund und Keystore (greift defensiv auf die Laufzeit zu, damit die App ohne das Plugin unveraendert weiterlaeuft), die Pfad-Freigabeliste fuer den Reverse Proxy und das Geruest der FMM003-Zuordnungstabelle. Die QR-Seite fuer die Einrichtung war zunaechst ein Fehlgriff: Ich hatte den QR-Erzeuger selbst geschrieben. Der Vergleich gegen eine erprobte Implementierung zeigte 1239 abweichende Module von 3249 - der Code waere unlesbar gewesen. Jetzt liegt eine bewaehrte Bibliothek (MIT, 57 KB) neben der Seite im eigenen Home Assistant. Das erfuellt die eigentliche Anforderung genauso: kein Netzzugriff, der Token verlaesst das eigene Netz nicht. Geprueft ueber einen Umlauf - 233 Zeichen hinein, identisch wieder heraus.
4.7 KiB
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.
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.
Was die App wirklich braucht
Aus dem tatsächlichen Code der Datenschicht (companion-app/src/api/)
abgeleitet, nicht geschätzt:
| 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 (Upgrade) |
/api/websocket |
Live-Aktualisierung |
Alles andere wird geblockt, insbesondere /auth/*, /lovelace*, /config*,
/api/config, /api/history*, /developer-tools* und die 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.
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.
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).
# Reihenfolge zählt: die erlaubenden Blöcke stehen vor dem pauschalen Verbot.
location = /api/ {
proxy_pass http://homeassistant:8123;
include conf.d/include/proxy.conf;
}
location ~ ^/api/states/pyscript\.(audi_dashboard_[a-z_]+|reifen_[a-z_]+)$ {
limit_except GET { deny all; }
proxy_pass http://homeassistant:8123;
include conf.d/include/proxy.conf;
}
location ~ ^/api/services/pyscript/audi_dashboard_[a-z_]+$ {
limit_except POST { deny all; }
proxy_pass http://homeassistant:8123;
include conf.d/include/proxy.conf;
}
location = /api/websocket {
proxy_pass http://homeassistant:8123;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
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; }
proxy_pass http://homeassistant:8123;
include conf.d/include/proxy.conf;
}
# Alles Übrige: nicht durchlassen.
location / {
return 404;
}
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.
Nach der Einrichtung prüfen
Von einem Netz ohne VPN, etwa über Mobilfunk:
API=https://api.datametric360.app
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"
# muss 404 oder 403 liefern
curl -s -o /dev/null -w '%{http_code}\n' "$API/auth/authorize"
curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $T" "$API/api/config"
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"
Zusätzlich: Port 8123 darf von außen gar nicht antworten.
Offen bis zur Einrichtung
- Nameserver von
datametric360.appauf 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