RustDesk Web-Client hinter dem Nginx Proxy Manager: Fernwartung im Browser

Mit einem selbst gehosteten RustDesk-Server kannst du Rechner auch ohne installierte App fernwarten, denn der Web-Client läuft direkt im Browser. Sobald er hinter einem Reverse Proxy mit HTTPS laufen soll, gibt es aber ein paar Stolperfallen, die in der Doku kaum stehen. Dieser Beitrag zeigt das Setup mit dem Nginx Proxy Manager (NPM) und den typischen Fehlern.
Alle Domains und IPs sind Beispiele. Ersetze
example.orgund die172.16.0.x-Adressen durch deine eigenen.
Was am Ende läuft
| Domain | Ziel | Zweck |
|---|---|---|
fernwartung.example.org | Web-Client, Port 5000 | Weboberfläche |
api.fernwartung.example.org | rustdesk-api, Port 21114 | API und Admin-Oberfläche |
ws-id.fernwartung.example.org | hbbs, Port 21118 | WebSocket zum ID-Server |
ws-relay.fernwartung.example.org | hbbr, Port 21119 | WebSocket zum Relay |
Der Server (hbbs/hbbr) läuft bei mir als Container unter 172.16.0.49, die API unter 172.16.0.50, der Web-Client unter 172.16.0.51.
Warum vier Domains?
Der Web-Client baut die Verbindung zum ID-Server als wss://<domain>:21116 auf. Auf 21116 lauscht hbbs aber mit seinem rohen TCP-Dienst, ohne TLS und ohne WebSocket. Der Browser bekommt keine Verbindung, und in der Konsole erscheinen Folgefehler wie curConn is undefined. Die eigentlichen WebSocket-Ports sind 21118 (hbbs) und 21119 (hbbr), sie sprechen aber unverschlüsseltes WebSocket, während die Seite über HTTPS läuft. Deshalb terminiert der NPM das TLS und bekommt dafür zwei eigene Subdomains.
Schritt 1: DNS
Lege für alle vier Domains A-Records auf die öffentliche IP des NPM-Hosts an und prüfe sie:
bash
dig +short ws-id.fernwartung.example.org
Erst wenn jede Domain die richtige IP liefert, fordere Zertifikate an, sonst scheitert die Let’s-Encrypt-Challenge.
Schritt 2: Web-Client per Docker Compose
Ich nutze das Image pmietlicki/rustdesk-web-client:v1.
yaml
rustdesk-webclient:
container_name: rustdesk-webclient
image: pmietlicki/rustdesk-web-client:v1
restart: unless-stopped
environment:
- CUSTOM_RENDEZVOUS_SERVER=ws-id.fernwartung.example.org:443
- RELAY_SERVER=ws-relay.fernwartung.example.org:443
- KEY=<OEFFENTLICHER_SCHLUESSEL>
- API_SERVER=https://api.fernwartung.example.org
ports:
- 5000:5000
networks:
docknet:
ipv4_address: 172.16.0.51Drei Details entscheiden, ob es klappt:
- Das Image
:v1lauscht intern auf Port 5000, nicht auf 80. Mit5000:80läuft der Container, aber die Seite bleibt leer. - Die Variablen heißen
CUSTOM_RENDEZVOUS_SERVER,RELAY_SERVER,KEYundAPI_SERVER.RUSTDESK_KEYgehört zu einem anderen Image und wird ignoriert. KEYist der öffentliche Schlüssel aus der Dateiid_ed25519.pubim Datenverzeichnis von hbbs.
Testen, bevor es an den NPM geht:
bash
docker compose up -d --force-recreate rustdesk-webclient curl -I http://127.0.0.1:5000/ # erwartet: 200
Schritt 3: Proxy Hosts im NPM
Lege für jede Domain einen Proxy Host an:
| Domain | Scheme | Ziel | Port |
|---|---|---|---|
fernwartung.example.org | http | 172.16.0.51 | 5000 |
api.fernwartung.example.org | http | 172.16.0.50 | 21114 |
ws-id.fernwartung.example.org | http | 172.16.0.49 | 21118 |
ws-relay.fernwartung.example.org | http | 172.16.0.49 | 21119 |
Bei allen: Websockets Support an, neues Let’s-Encrypt-Zertifikat, Force SSL und HTTP/2 an. HSTS schaltest du erst nach erfolgreichem Test ein. Bei den beiden WebSocket-Hosts kommt im Zahnrad-Tab noch dies hinein, damit ruhende Sitzungen nicht nach 60 Sekunden getrennt werden:
nginx
proxy_read_timeout 3600s; proxy_send_timeout 3600s;
Schritt 4: Testen
bash
curl -I https://fernwartung.example.org # 200 curl -I http://fernwartung.example.org # 301 auf https curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" \ -H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: SGVsbG8sIHdvcmxkIQ==" \ https://ws-id.fernwartung.example.org/ # 101 Switching Protocols
Öffne den Web-Client danach in einem privaten Fenster. Er legt seine Einstellungen im localStorage ab und lädt per Service Worker, sonst hängen alte Werte fest.
Schritt 5: Die Ports 21118 und 21119 schließen
Läuft alles über 443, müssen 21118 und 21119 nicht mehr öffentlich erreichbar sein. Die RustDesk-Doku empfiehlt, sie so einzuschränken, dass nur der Reverse Proxy zugreifen kann. Vorher sicherst du die Firewall-Konfiguration:
bash
ufw status numbered > ~/ufw-status-$(date +%F).txt tar czf ~/ufw-backup-$(date +%F).tar.gz /etc/ufw /etc/default/ufw
Dann entfernst du die Freigaben für die beiden Ports (bei mir Routing-Regeln zu 172.16.0.49) und lässt 21115 bis 21117 sowie 21116/udp bestehen. Zusätzlich löschst du in der Compose des RustDesk-Servers die beiden ports:-Zeilen für 21118 und 21119. Der NPM erreicht die Ports auch ohne Veröffentlichung über das Docker-Netzwerk. Zum Schluss testest du von außerhalb deines Netzes (ein Test vom Server selbst sagt nichts, weil er die Firewall umgeht):
bash
nc -zv -w 5 <öffentliche-IP> 21118 # Timeout erwartet nc -zv -w 5 <öffentliche-IP> 21117 # Erfolg erwartet
Typische Fehler
301auf die Hauptdomain oderSSL_ERROR_UNRECOGNIZED_NAME_ALERT: Der NPM, der auf 80/443 antwortet, kennt die Domain nicht. Der Proxy Host fehlt, liegt in einer anderen NPM-Instanz oder ist inaktiv. Auf dem Host hilftdocker exec <npm> grep -rl <domain> /config/nginx/proxy_host/.- NPM-Log mit
Permission DeniedundCannot read properties of null (reading 'id'): Die Sitzung der Weboberfläche ist ungültig, und der Proxy Host wurde nicht gespeichert. Abmelden, neu anmelden, Host neu anlegen. Could not resolve host: Der DNS-Eintrag fehlt noch.- HTTP liefert
200statt eines Redirects: Force SSL ist nicht aktiv.
Offener Punkt
Bei mir lief der Web-Client danach, den Login mit Benutzername und Passwort gegen die API habe ich darin nicht zum Laufen bekommen, obwohl POST /api/login per curl 200 liefert und die Admin-Oberfläche erreichbar ist. Als Kandidaten stehen Mixed Content (API_SERVER mit https:// angeben), CORS und alte Werte im Browser im Raum. Falls du die Ursache findest, lass es mich gern wissen.
Fazit
Der Kern ist, dass der Web-Client nicht mit 21116 zurechtkommt und stattdessen WebSocket über TLS braucht. Mit zwei zusätzlichen Subdomains im NPM läuft das ohne neue öffentliche Ports, und die WebSocket-Ports lassen sich anschließend wieder schließen.
#docker #fernwartung #rustdesk #webgui