7.2 KiB
AppRobotWebcam – Port-Forwarding / HTTPS-Proxy Roadmap
Ziel: Die Viewer-Webseite über einen HTTPS-Reverse-Proxy nach aussen geben, ohne go2rtc oder interne Ports ins Internet zu hängen. Stand: 2026-06-04 · noch nicht umgesetzt (Plan zum Abarbeiten).
TL;DR — welche Ports nach aussen?
| Port | Dienst | Internet? | Begründung |
|---|---|---|---|
| 443 | HTTPS-Reverse-Proxy | ✅ ja – einziger Internet-Port | TLS-Terminierung + einziger Einstieg |
| 8444 | Node.js (Webseite, /api, Snapshots, Stream-WS) |
nur Proxy→Backend (LAN/localhost) | Proxy leitet hierhin weiter; nicht ins Internet |
| 1984 | go2rtc API / WebSocket / Debug-UI | ❌ nein | bleibt intern (localhost) |
| 8555/udp | go2rtc WebRTC-Media | ❌ nein | im aktuellen MJPEG-Modus ungenutzt (siehe Caveat unten) |
Merksatz: Nach Umsetzung dieser Roadmap ist der einzige offene Port 443 am Proxy.
Alles andere läuft über genau eine Origin (https://<host>) → Proxy → 8444.
network_mode: host: Die Container binden direkt an Host-Ports — es gibt kein Docker-ports:-Mapping. „Offen/zu" steuerst du allein über die Host-Firewall. Läuft der Proxy auf demselben Host, muss8444gar nicht in der Firewall geöffnet werden (Proxy erreicht127.0.0.1:8444).
Aktueller Stand (was läuft)
- Live-Bild kommt als MJPEG über WebSocket (
MODE = 'mjpeg',public/viewer.js:9). Die WebRTC-Kommentare imdocker-compose.yamlbeschreiben einen anderen Aufbau, der nicht aktiv ist → UDP 8555 wird derzeit nicht gebraucht. - Node.js (
8444) liefert: Webseite,/config.json,/health,/api/snapshot/*und proxied/api,/video-rtc.js,/video-stream.jsper HTTP an go2rtc (server.js:34). - Snapshots und Skripte laufen bereits relativ/same-origin über
8444→ proxy-tauglich.
Das Problem (warum es so noch nicht hinter HTTPS läuft)
Der Viewer baut die Stream-Verbindung aktuell direkt zu go2rtc auf — public/viewer.js:36:
const wsUrl = `ws://${location.hostname}:${GO2RTC_PORT}/api/ws?src=...`; // GO2RTC_PORT = 1984
Hinter einem HTTPS-Proxy scheitert das doppelt:
- Mixed Content — eine
https://-Seite darf kein unverschlüsseltesws://öffnen → Browser blockt die Verbindung hart. - Proxy-Umgehung — die URL zeigt direkt auf Port
1984, den wir bewusst nicht exponieren (dort hängt auch die offene go2rtc-Debug-UI).
→ Folge: Seite lädt, aber kein Live-Bild.
Lösung — 3 Schritte
Danach geht der Stream über dieselbe Origin wie die Seite (wss://<host>/api/ws),
durch den Proxy, auf 8444, intern weiter zu go2rtc 1984.
Schritt 1 — public/viewer.js: same-origin & protokoll-bewusste WS-URL
startStream(), aktuell viewer.js:36:
// ALT:
const wsUrl = `ws://${location.hostname}:${GO2RTC_PORT}/api/ws?src=${encodeURIComponent(cam.id)}`;
// NEU:
const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${proto}//${location.host}/api/ws?src=${encodeURIComponent(cam.id)}`;
location.hostenthält ggf. den Port → funktioniert sowohl direkt im LAN (http://host:8444→ws://host:8444/...) als auch hinter HTTPS (https://cam.example.com→wss://cam.example.com/...)./api/wsist im Proxy-pathFilterbereits enthalten (server.js:37).GO2RTC_PORT/ der/config.json-Fetch ininit()werden für den Stream damit nicht mehr gebraucht (dürfen als harmloser Toter Code bleiben oder raus).
Schritt 2 — server.js: Proxy WebSockets durchreichen lassen
Im createProxyMiddleware-Block (server.js:34) ws: true ergänzen:
const go2rtcProxy = createProxyMiddleware({
target: GO2RTC_URL,
changeOrigin: true,
ws: true, // ← NEU
pathFilter: ['/api', '/video-rtc.js', '/video-stream.js'],
logger: console,
on: { /* … unverändert … */ },
});
Und nach dem Erstellen des HTTP-Servers (server.js:113) den Upgrade-Handler binden:
const server = http.createServer(app);
server.on('upgrade', go2rtcProxy.upgrade); // ← NEU (WebSocket-Upgrades an go2rtc)
http-proxy-middleware@^3(vorhanden,package.json):ws: trueplus der expliziteserver.on('upgrade', …)ist die dokumentierte, zuverlässige Kombination.
Schritt 3 — Reverse-Proxy: WebSocket-Upgrade durchreichen
nginx:
server {
listen 443 ssl;
server_name cam.example.com;
# ssl_certificate … / ssl_certificate_key …;
location / {
proxy_pass http://127.0.0.1:8444;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; # ← Pflicht für WS
proxy_set_header Connection "upgrade"; # ← Pflicht für WS
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 3600s; # WS-Stream offen halten
}
}
Synology DSM (Systemsteuerung → Anmeldeportal → Erweitert → Reverse Proxy):
- Regel anlegen: Quelle
HTTPS/cam.example.com/443→ ZielHTTP/localhost/8444. - In der Regel → Reiter Eigene Kopfzeile (Custom Header) → Erstellen → WebSocket
(fügt
Upgrade/Connectionautomatisch hinzu). Ohne diesen Schritt kommt kein Bild. - Optional Timeout erhöhen, damit der Stream nicht nach kurzer Zeit getrennt wird.
Caddy (zur Referenz — WS geht automatisch):
cam.example.com {
reverse_proxy 127.0.0.1:8444
}
Verifikation (nach Deploy abhaken)
https://cam.example.com/lädt, Header + Kamera-Kacheln sichtbar.- DevTools → Console:
[WebcamViewer][init] … <video-stream> definiert, kein Mixed-Content-Fehler. - DevTools → Network → Filter „WS": Eintrag
wss://cam.example.com/api/ws?src=cam0mit Status 101 Switching Protocols (nicht 4xx/blocked). - Live-Bild für cam0 und cam1 läuft; Status zeigt „MJPEG · live".
- „⬇ Snapshot alle" lädt JPG(s) herunter (
/api/snapshot/...über Proxy). - Von aussen: nur
443erreichbar.1984,8444,8555aus dem Internet nicht erreichbar (z. B.curl https://<public>:1984→ Timeout/refused).
Rollback
Kleine, isolierte Änderung in genau zwei Dateien — risikoarm:
public/viewer.jsundserver.jsauf den alten Stand zurück (git).docker restart AppRobotWebcam(lädt Code neu).
Der Live-Stream im LAN funktioniert mit der neuen viewer.js weiterhin
(ws://host:8444/...), d. h. die Änderung ist auch ohne Proxy gefahrlos.
Caveat — falls je wieder WebRTC (MODE mit webrtc)
Wird public/viewer.js jemals zurück auf MODE = 'webrtc,mse,mjpeg' gestellt
(siehe doc/04_Delay_roadmap.md), ändert sich die Port-Lage:
- WebRTC-Signaling läuft weiter über die WebSocket (
/api/ws) → durch den Proxy ok. - WebRTC-Media läuft über UDP 8555 und geht nicht durch einen HTTP(S)-Proxy. Dann bräuchte man entweder UDP 8555 direkt erreichbar oder einen TURN-Server.
Für den aktuellen MJPEG-Betrieb ist das irrelevant — alles läuft über TCP/WS auf 8444.