RustDesk OSS mit eigener Verwaltung / Technische Betriebshinweise
=================================================================

Diese Datei beschreibt Aufbau, Build, Tests und Betrieb. Platzhalter:
  remote.example.org   Hostname der eigenen Instanz (instance.json: Server)
  <Stack>              Installationsverzeichnis auf dem Server
                       (instance.json: Stack, Standard /opt/rustdesk)
  <Hersteller>         Ordnername auf den Clients (instance.json: VendorFolder)

Alle instanzabhaengigen Werte stehen in instance.json (nicht im Repository;
Vorlage instance.example.json): Server, PublicKey, Organization, ProductName,
LoginProvider, VendorFolder, BundleIdPrefix, Stack. install.py legt die Datei
bei der Einrichtung an und traegt den oeffentlichen Serverschluessel ein.


Einrichtung und wiederkehrende Aufgaben (install.py)
----------------------------------------------------
Auf dem Linux-Server (Debian/Ubuntu) als root im Quellverzeichnis:
  python3 install.py                 gefuehrte Einrichtung
  python3 install.py windows         Windows-Setup neu bauen und veroeffentlichen
  python3 install.py download-page   Downloadseite neu erzeugen (z. B. nach
                                     neuer setup.dmg)
Optionen: --stack VERZEICHNIS, --email ADRESSE (Let's Encrypt).

Die gefuehrte Einrichtung fragt die Namen ab, legt <Stack> mit data/,
management-data/, management-secret/, downloads/ und bootstrap/ an, kopiert
server/compose.yml (nur wenn noch keine vorhanden ist) und schreibt .env
(SERVER, ORGANIZATION, PRODUCT_NAME, LOGIN_PROVIDER). Danach baut sie
hbbs/hbbr mit den Patches aus server/secure-hbbs (auf einem kleinen Server
20-40 Minuten) und das Verwaltungs-Image, initialisiert die Verwaltung
(app.py init), startet den Stack, liest den oeffentlichen Schluessel aus
data/id_ed25519.pub, richtet nginx und das TLS-Zertifikat (certbot, webroot)
ein, baut das Windows-Setup und erzeugt die Downloadseite.

Jeder Schritt ist wiederholbar: vorhandene Images, Konfiguration, Schluessel
und Zertifikate bleiben erhalten. Eine vorhandene compose.yml, die von der
Vorlage abweicht, wird nie ersetzt; Aenderungen der Vorlage von Hand
uebernehmen.

Erste Anmeldung: <Stack>/bootstrap/Admin-Zugang.txt enthaelt Benutzer "admin"
und ein einmaliges Startpasswort. Damit anmelden, sofort einen Passkey
einrichten und den Wiederherstellungsschluessel ausdrucken; danach ist die
Passwortanmeldung abgeschaltet, die Datei loeschen.
<Stack>/bootstrap/Download-Zugang.txt enthaelt das Download-Passwort fuer die
betreuten Personen. <Stack>/bootstrap/mac-build.json enthaelt nur den
Registrierungsschluessel fuer einen Mac-Build.

Admin-Client einmalig konfigurieren (Netzwerk):
  ID-Server:    remote.example.org
  Relay-Server: remote.example.org:21117
  API-Server:   https://remote.example.org
  Key:          Inhalt von <Stack>/data/id_ed25519.pub
WebSocket einschalten. Danach "Anmelden" -> Anmeldeknopf (LoginProvider)
waehlen und im Browser mit dem Passkey bestaetigen. Oben ein Adressbuch oder
eine Liste (Stern) waehlen.


Ein Programm fuer beide Wege
----------------------------
setup.exe (Windows) und setup.dmg (macOS) fragen nach dem Oeffnen:
  "Jetzt Hilfe bekommen"  ohne Installation und ohne Administratorrechte;
                          RustDesk zeigt ID und Einmalpasswort bzw. die
                          Person nimmt die Verbindung per Klick an. Nichts
                          wird installiert oder an die Verwaltung gemeldet.
  "Dauerhaft einrichten"  Windows: Neustart des Setups per UAC mit /install;
                          macOS: Administratorabfrage.

Die Hilfe ohne Installation schreibt die Serveroptionen direkt in die
Benutzer-Konfiguration von RustDesk (Windows
%APPDATA%\RustDesk\config\RustDesk2.toml, macOS
~/Library/Preferences/com.carriez.RustDesk/RustDesk2.toml), weil RustDesk
--option nur installiert und mit Adminrechten annimmt. portable/config_merge.c
(gemeinsam fuer Windows und macOS, build.py kopiert sie nach client/) ersetzt
nur die verwalteten Eintraege der Tabelle [options], ersetzt die Datei atomar
und lehnt Verknuepfungen, Reparse-Points und Dateien ueber 1 MiB ab. Die
Optionen stehen in client-options.json ("common", fuer die Hilfe ohne
Installation mit den Abweichungen aus "portable": nur Einmalpasswort, Annahme
per Klick moeglich).


Windows-Setup
-------------
Das Setup (Version: client/settings.json, SetupVersion) enthaelt den
unveraenderten offiziellen RustDesk-Client (ClientVersion, SHA256 fest
hinterlegt) und konfiguriert ihn ueber dessen native CLI. Die Einrichtung
laeuft in einer nativen Windows-x64-EXE aus C (client/native-config.c) ohne
PowerShell oder .NET. Das NSIS-Setup selbst laeuft mit Benutzerrechten; nur
"Dauerhaft einrichten" startet es per UAC neu.

Ablauf der dauerhaften Einrichtung: Paketpruefsumme (BCrypt SHA256),
Installation oder Wiederverwendung derselben Version (neuere werden nicht
heruntergestuft), Dienststart, Setzen und Zuruecklesen der Optionen aus
client-options.json, Auslesen der ID, neues zufaelliges Passwort (20 Zeichen,
120 Bit), Meldung von ID, Passwort, Rechnername, Clientversion, Plattform und
Geraeteschluessel per HTTPS an /enroll/v1/device. Windows-Zertifikatspruefung,
keine Weiterleitungen. Die Abschlussseite zeigt ID und Passwort; Kopieren in
die Zwischenablage nur auf Klick.

Bei erneuter Ausfuehrung warnt das Setup vor dem Umstellen des Servers und dem
Ersetzen eines vorhandenen Passworts. Deinstallation ueber den normalen
RustDesk-Eintrag in Windows Apps.

Diagnose: C:\ProgramData\<Hersteller>\setup.log, ohne Passwoerter. Das Setup
uebernimmt Besitz und Zugriffsrechte des Ordners (SYSTEM/Administratoren
Vollzugriff, Benutzer lesen) und folgt keinen Links, denn unter ProgramData
koennen Standardbenutzer Ordner, Links und Dateien anlegen; eine von ihnen
angelegte Datei wird ersetzt. Ohne vertrauenswuerdige Logdatei laeuft das
Setup ohne Log weiter.

Geraeteschluessel: ein Zufallsgeheimnis nur fuer SYSTEM/Administratoren in
C:\ProgramData\<Hersteller>\device.key (macOS: root-only unter
/Library/Application Support/<Hersteller>/device.key). Es wird bei jeder
Registrierung mitgesendet; der Server speichert nur einen HMAC.

Der native Runner wartet auf das Prozesshandle, nie auf Pipe-EOF, und begrenzt
vererbte Handles: RustDesk-Hintergrundprozesse koennen die NSIS-Ausgabe nicht
offen halten. Zeitlimits dienen nur als Fehlerabsicherung.

setup.exe wird nicht mit einem Code-Signing-Zertifikat signiert; SmartScreen
warnt deshalb beim ersten Start.

Bauen auf Linux mit Docker und Python 3 (install.py windows erledigt das
und veroeffentlicht setup.exe, setup.exe.sha256 und die Lizenz in
<Stack>/downloads):
  python3 build.py --private-config <Stack>/management-secret/config.json
Ohne --private-config wird ../management-secret/config.json erwartet. Die
Datei enthaelt den Registrierungsschluessel; der Build laeuft deshalb auf dem
Server. build.py prueft den SHA256 des offiziellen Clients, erzeugt
client/settings.h (Server, oeffentlicher Schluessel, Herstellerordner,
Versionen, Optionen, Registrierungsschluessel; nicht im Repository und nicht
im Quellenarchiv), kompiliert im Image rustdesk-setup-builder mit MinGW
(-Werror, ohne Netzwerk) und baut mit NSIS eine einzelne setup.exe. NSIS
erhaelt PRODUCT, SERVER, VENDOR und SETUP_VERSION als Defines.

Native Tests unter Wine: Image aus client/tests/Dockerfile bauen (setzt das
Image rustdesk-setup-builder voraus, also zuerst build.py):
  docker build -t rustdesk-setup-tests client/tests
In einem Temporaerverzeichnis native-config.c, settings.h, config_merge.c/.h,
tests/*.c und rustdesk.exe ablegen, test.c (-Wno-unused-function, zusammen
mit config_merge.c und den Bibliotheken aus build.py) und fixture.c mit
x86_64-w64-mingw32-gcc bauen und
  wine64 test.exe Z:\checks\fixture.exe Z:\checks\rustdesk.exe
ausfuehren (Verzeichnis als /checks einbinden). Geprueft werden Prozess-
abschluss trotz gehaltener Ausgabe, Argument-Quoting, grosse/leere Ausgaben,
Fehler/Zeitlimit, Optionsrueckmeldung, Pruefsumme, Passwoerter, INI-Uebergabe
und JSON-Escaping. Sie ersetzen keinen Test auf einem echten Windows-PC.
config_merge.c hat eigene Tests (Aufruf im Kopf von
portable/test_config_merge.c).

macOS-Setup: siehe macos/README.md (Build auf einem Mac mit Xcode und
Apple-Developer-Konto; instance.json und bootstrap/mac-build.json vom Server
mitnehmen). Die DMG als <Stack>/downloads/setup.dmg (+ .sha256) ablegen und
python3 install.py download-page ausfuehren.


Ablauf fuer Techniker: Hilfe-Sitzung und dauerhafte Einrichtung
---------------------------------------------------------------
1. Die Person laedt das Programm von der Download-Seite, waehlt "Jetzt Hilfe
   bekommen" und nennt ID und Einmalpasswort (oder nimmt die Verbindung an).
   Mac beim ersten Mal: in RustDesk "Konfigurieren", "RustDesk" fuer
   Bildschirmaufnahme und Bedienungshilfen einschalten, "Beenden & erneut
   oeffnen".
2. Soll der Rechner dauerhaft erreichbar sein: Die Person klickt im Fenster
   "Die Fernhilfe laeuft" auf "Dauerhaft einrichten".
   - Windows: Es erscheint die UAC-Abfrage. Der Techniker sieht sie nicht;
     RustDesk meldet im Admin-Client, dass eine Rechteerhoehung noetig ist.
     Hat die Person Adminrechte: warten, die Person klickt auf "Ja".
     Hat sie keine: im Admin-Client "Mit Benutzername und Passwort erhoehen"
     und ein Admin-Konto eingeben.
   - Mac: Die Person gibt das Mac-Administratorkennwort selbst ein.
3. Die Verbindung bricht ab, waehrend das Setup RustDesk ersetzt. Nach etwa
   einer halben Minute ist der Rechner in der Verwaltung gemeldet: im eigenen
   RustDesk-Client im Adressbuch "Neu (ungeprueft)" neu verbinden (ohne
   Passworteingabe), danach in der Weboberflaeche einem Adressbuch zuordnen.
   Auf dem Mac gelten die in Schritt 1 erteilten Freigaben weiter.
4. Wird ein bereits bekannter Rechner neu eingerichtet, uebernimmt die
   Verwaltung das neue Passwort automatisch, wenn der Geraeteschluessel
   passt; sonst erscheint es unter "Gemeldete Aenderungen" und muss dort
   uebernommen werden.


Serverbetrieb
-------------
  cd <Stack>
  docker compose ps
  docker compose logs --tail 50 management
  docker compose up -d --no-deps management

Compose-Projekt "rustdesk" mit hbbs, hbbr und management; Werte aus .env.
Ein Neustart von hbbr beendet laufende Fernsitzungen; ein Neustart von hbbs
laesst Geraete fuer einige Sekunden neu registrieren. hbbs/hbbr daher nie
nebenbei neu erstellen, sondern nur bewusst einzeln
(docker compose up -d --no-deps hbbr bzw. hbbs). Kein "docker compose up -d"
ohne Dienstnamen nach Aenderungen an compose.yml, wenn nur die Verwaltung
gemeint ist. Alle Container laufen read-only, ohne Capabilities und mit
no-new-privileges.

Extern erforderlich: TCP 80, 443, 21115, 21116, 21117; UDP 21116.
WebSocket-Ports 21118/21119 und die API 21114 sind nur auf 127.0.0.1
gebunden. nginx (/etc/nginx/sites-available/rustdesk-remote.example.org.conf
und snippets/rustdesk-{api,ws}.conf, erzeugt aus server/nginx*.conf) stellt
/ws/id, /ws/relay, /api/, /manage/ (Kurzform /admin), /enroll/, /download/
sowie /setup.exe und /setup.dmg ueber HTTPS bereit und setzt X-Real-IP
selbst. Statische Dateien kommen aus <Stack>/downloads; EXE/DMG gibt es nur
ueber eine Download-Sitzung.

Backup: compose.yml, .env, data/ (privater Serverschluessel),
management-data/ und management-secret/ gemeinsam sichern, dazu instance.json.
Die Datenbank mit der SQLite-Backup-Funktion sichern, nicht als Dateikopie
waehrend des Betriebs, z. B.:
  python3 -c "import sqlite3; s=sqlite3.connect('management-data/management.sqlite3'); \
    d=sqlite3.connect('backup.sqlite3'); s.backup(d); d.close()"
Der Serverschluessel muss bei Updates erhalten bleiben, sonst passen die
vorhandenen Setups nicht mehr. Private Schluessel nie nach downloads/ kopieren.

Konto-Wiederherstellung und Umbenennen (Konsole, im Verzeichnis <Stack>):
  docker compose exec management python app.py recovery KONTONAME
erzeugt einen 15 Minuten gueltigen Einmal-Link zum Einrichten eines Passkeys
(fuer das eigene bzw. letzte Admin-Konto).
  docker compose exec management python app.py rename ALT NEU


Update der Verwaltung (ohne laufende Sitzungen zu trennen)
----------------------------------------------------------
Nur der Container management wird neu gestartet; hbbs und hbbr bleiben
unberuehrt.
1. Neue Quellen nach management/ uebernehmen, Image mit neuer Versionsnummer
   bauen (Tag in server/compose.yml und <Stack>/compose.yml anpassen):
     docker build -t rustdesk-management:X.Y.Z management
   Neue Python-Module muessen in der COPY-Zeile des Dockerfile stehen.
   Pruefen: docker run --rm --entrypoint python rustdesk-management:X.Y.Z \
     -c "import vault, presence"   (app.py braucht zum Import config.json)
2. Tests im neuen Image (siehe "API-Tests"); nur bei "OK" weitermachen.
3. Datenbank mit der SQLite-Backup-Funktion sichern (siehe Backup), zusammen
   mit management-secret/ und compose.yml in ein Verzeichnis mit Version und
   Zeitstempel.
4. Optional das neue Image probeweise gegen eine Kopie der gesicherten
   Datenbank starten (eigene Volumes, kein Port), Log pruefen.
5. compose.yml pruefen (docker compose config -q), dann nur die Verwaltung
   neu starten:
     docker compose up -d --no-deps management
     docker compose logs --tail 50 management
Rueckweg: alten Image-Tag in compose.yml eintragen, gesicherte Datenbank
zurueckspielen, erneut nur management neu starten. Schemaaenderungen sind
rein additiv; eine aeltere Version kann eine neuere Datenbank aber nicht
garantiert lesen, daher immer zuerst sichern.


Verwaltung / Adressbuch (eigene kompatible API, kein RustDesk Server Pro)
------------------------------------------------------------------------
URL: https://remote.example.org/manage/ (Kurzform /admin).
Umgebung: PUBLIC_URL, ORGANIZATION, PRODUCT_NAME, LOGIN_PROVIDER (aus .env),
optional CLIENT_SESSION_HOURS (Standard 24), HBBS_ADDRESS (Standard
hbbs:21115), PRESENCE_SECONDS (Standard 30, 0 schaltet ab).

Anmeldung: nur Passkey. Das Startpasswort aus der Erstinstallation dient
ausschliesslich dazu, den ersten Passkey einzurichten; danach lehnen Web und
/api/login Passwoerter ab und bestehende Passwort-Sitzungen enden. Der
RustDesk-Client nutzt den Browser-Ablauf /api/oidc/auth und
/api/oidc/auth-query; bestaetigt wird auf /manage/#client-login=... mit dem
Passkey. Ein Client-Token wird genau einmal ausgegeben.

Passkey verloren (Weboberflaeche, Benutzer-Editor im Admin-Modus):
"Wiederherstellungslink erstellen" (24 Stunden, einmalig; die Person braucht
zusaetzlich ihren ausgedruckten Wiederherstellungsschluessel, alles bleibt
erhalten) oder "Konto zuruecksetzen" (auch der Ausdruck ist weg: Passkeys,
Sitzungen und Freigaben enden, Einladungslink fuer neuen Passkey und
Schluessel, danach erneut bestaetigen; nur "Mein Adressbuch" im Client geht
verloren). Fuer das eigene bzw. letzte Admin-Konto bleibt die Konsole (siehe
Serverbetrieb).

Konten: ein Konto pro Person, Admin-Rechte als Haekchen. Admin-Funktionen
gelten nur im Admin-Modus: Menue "Admin", Passkey bestaetigen, gueltig 15
Minuten nach der letzten Admin-Aktion. Neue Konten erhalten die Schluessel
erst, wenn ein Admin sie unter "Benutzer" nach Abgleich des Schluessel-
Fingerabdrucks bestaetigt (Tabelle endorsements: MAC mit dem jeweiligen
Schluessel; ohne Schluessel nicht faelschbar).

Online-Status: Die Verwaltung fragt hbbs alle PRESENCE_SECONDS Sekunden ueber
den NAT-Test-Port 21115 (OnlineRequest, wie der RustDesk-Client) nach allen
IDs und merkt sich "zuletzt online" (devices.last_seen). Nur lesend; erreicht
weder die Geraete noch laufende Sitzungen. "Verbinden" oeffnet
rustdesk://connection/new/ID im installierten Client, ohne Passwort im Link.

Geraeteschluessel: Stimmt der mitgesendete Schluessel bei einer erneuten
Einrichtung, wird das neue Passwort ohne Admin uebernommen; sonst bleibt es
eine gemeldete Aenderung. Bestehende Geraete ohne Schluessel binden ihn beim
naechsten Lauf ueber die Bestaetigung eines Admins oder ihr aktuelles
Passwort.

Neue Rechner erscheinen automatisch mit dem Tag "Neu". Das ist eine
Geraetemeldung, kein Identitaetsnachweis oder Online-Status. Ein geaendertes
Passwort fuer eine bekannte ID wird nur vorgemerkt und erst nach
ausdruecklicher Uebernahme verwendet; angezeigt werden gemeldeter Rechnername,
Adresse und Plattform. Anmeldung, Registrierung und Passkey-Vorgaenge sind
rate-limitiert. Das Aktivitaetsprotokoll enthaelt die letzten 10.000 Ereignisse.

Download-Passwort, Admin-Anmeldung und Registrierung sind getrennte
Berechtigungen. Download-Sitzungen gelten eine Stunde; es gibt keinen
ungeschuetzten statischen EXE-/DMG-Pfad.

SQLite: <Stack>/management-data/management.sqlite3
Registrierungsschluessel und Passwort-Hashes: <Stack>/management-secret/config.json.

Schluesselspeicher (je Konto bei der Ersteinrichtung angelegt):
Geraetepasswoerter, gemeldete Aenderungen und das persoenliche Adressbuch sind
mit X25519 versiegelt (vault.py). Der private Schluessel liegt nur verpackt
vor: je Passkey (WebAuthn-PRF), mit dem ausgedruckten Wiederherstellungs-
schluessel und je Sitzung mit einem aus dem Token abgeleiteten Schluessel.
Ein Mandantenschluessel versiegelt die Passwoerter aller Adressbuecher und ist
an jedes aktive Konto versiegelt; ein Eingangsschluessel fuer neu gemeldete
Geraete und Aenderungen nur an Admin-Konten. Registrierungen brauchen nur den
oeffentlichen Schluessel und vergleichen bekannte Passwoerter ueber einen
HMAC-Fingerabdruck. Eine Datenbank aus der Zeit vor dem Schluesselspeicher
nutzt bis zur Einrichtung den bisherigen Fernet-Schluessel (encryption_key);
danach wird er aus config.json entfernt. Fingerabdruck- und WebAuthn-
Benutzerschluessel liegen in der Tabelle settings. Ohne Passkey und
Wiederherstellungsschluessel sind die Passwoerter verloren.
Schemaaenderungen sind rein additiv.

Die API richtet sich nach dem Client-Quellcode von 1.4.9/1.5.0. Erweiterte
Gruppen- und Mehrbenutzerrechte, LDAP, Pro-Funktionen und Browser-Fernzugriff
sind nicht enthalten. Das verwaltete Adressbuch ist im Client schreibgeschuetzt;
das persoenliche Adressbuch unterstuetzt einfache Peereintraege.

API-Tests (isoliertes temporaeres Verzeichnis, keine Produktionsdaten), im
fertigen Image, aus dem Quellverzeichnis:
  docker run --rm --user 0:0 -v "$PWD/management:/checks:ro" --entrypoint sh \
    rustdesk-management:X.Y.Z -c 'pip install --quiet cbor2 httpx \
    && cp /checks/test_api.py /app/ && cd /app && python -m unittest test_api'
test_api.py laedt app.py aus seinem eigenen Verzeichnis. Nach /app kopiert
prueft es also den Code im Image, und eine im Dockerfile (COPY) vergessene
Datei faellt auf; direkt aus /checks gestartet prueft es nur die Quellen.
Der Upgrade-Test braucht die Git-Historie (alte Version 1.3.0) und wird ohne
sie uebersprungen.
Geprueft u. a.: Schluesselspeicher (Einrichtung, falscher Wiederherstellungs-
schluessel, fehlendes PRF, Versiegelung aller Datensaetze, kein privater
Schluessel im Klartext in der Datenbank, gesperrte Sitzungen, weitere
Passkeys, Wiederherstellung nur mit Schluessel, versiegelte Client-Token),
Passkey-Registrierung/-Anmeldung mit Software-Authenticator, fremde Origin,
unbekannte Schluessel, Ceremony-Wiederverwendung, letzter Passkey,
Admin-Modus, Geraeteschluessel, Online-Status, Client-Browseranmeldung,
Adressbuch-Protokoll, Schutz bestehender Zugangsdaten, CSRF,
Download-Berechtigung, Rate-Limits, Instanznamen in den Seiten, Upgrade einer
Datenbank der Version 1.3 und Migration einer Altdatenbank.


Angemeldete RustDesk-Clients, WSS und Relay
------------------------------------------
Die Ergaenzungen von hbbs/hbbr (verschluesselter Handshake angemeldeter
Clients, dauerhafte WSS-Registrierung, atomare Relay-Zuordnung) sind in
server/secure-hbbs/README.txt beschrieben. Der vollstaendige Server-
Quellcode liegt nach dem Build als hbbs-source.tar.gz auf der Downloadseite
(AGPL), ebenso die Setup-Quellen ohne instance.json und Geheimnisse.

WSS ist eine explizite Transportwahl (allow-websocket=Y); der Client wechselt
nicht automatisch. Der unveraenderte Client 1.5.0 ignoriert im WebSocket-Modus
die eingetragene Proxy-Konfiguration; ein verpflichtender Firmenproxy wird
daher nicht unterstuetzt:
https://github.com/rustdesk/rustdesk/discussions/12723
Die nativen Ports bleiben fuer bestehende Installationen offen.
