[HIGH] Root-Command-Kanal Backend→Agent ohne erzwungenes TLS + Self-Update nur Hash (keine Signatur) #113

Closed
opened 2026-06-20 14:41:08 +00:00 by chinux · 4 comments
Owner

Schweregrad: HIGH (deployment-abhaengig: trifft zu, sobald ohne TLS betrieben)

Der Node-Agent laeuft als root und fuehrt jedes Backend-Command aus. Leitet sich SERVER_URL aus http:// ab, laeuft der gesamte Command-Kanal inkl. Klartext-Node-Token ueber ws:// → ein MITM liest das Token und injiziert Commands = Root-RCE auf allen Proxmox-Hosts.

Self-Update prueft nur einen mitgelieferten Hash (Integritaet, keine Signatur) → bietet keinen MITM-Schutz (Angreifer ersetzt Binary + Hash konsistent).

Belege: main.py:300 (URL-Ableitung), self_update.rs:30-40 (Hash), agent_ws.py:95 (WS-Auth).

Fix

  • wss:// erzwingen wenn nicht localhost; bei http://-Ableitung in Produktion Fehler/Warnung statt stilles ws://.
  • Server-Zertifikat im Agent verifizieren (optional pinnen).
  • Self-Update-Binaries signieren (ed25519; Public Key im Agent eincompiliert), Signatur statt blossem Hash pruefen.

Akzeptanz

  • Agent verweigert Klartext-ws:// ausser explizit erlaubtem localhost/Dev.
  • Self-Update akzeptiert nur signierte Binaries.

Quelle

Code-Review 20.06.2026 (verifiziert: URL-Ableitung + Hash-only).

## Schweregrad: HIGH (deployment-abhaengig: trifft zu, sobald ohne TLS betrieben) Der Node-Agent laeuft als **root** und fuehrt jedes Backend-Command aus. Leitet sich `SERVER_URL` aus `http://` ab, laeuft der gesamte Command-Kanal inkl. **Klartext-Node-Token** ueber `ws://` → ein MITM liest das Token und injiziert Commands = **Root-RCE auf allen Proxmox-Hosts**. Self-Update prueft nur einen mitgelieferten **Hash (Integritaet, keine Signatur)** → bietet **keinen** MITM-Schutz (Angreifer ersetzt Binary + Hash konsistent). Belege: `main.py:300` (URL-Ableitung), `self_update.rs:30-40` (Hash), `agent_ws.py:95` (WS-Auth). ## Fix - **`wss://` erzwingen** wenn nicht localhost; bei `http://`-Ableitung in Produktion **Fehler/Warnung** statt stilles `ws://`. - Server-Zertifikat im Agent **verifizieren** (optional pinnen). - **Self-Update-Binaries signieren** (ed25519; Public Key im Agent eincompiliert), Signatur statt blossem Hash pruefen. ## Akzeptanz - Agent verweigert Klartext-`ws://` ausser explizit erlaubtem localhost/Dev. - Self-Update akzeptiert nur signierte Binaries. ## Quelle Code-Review 20.06.2026 (verifiziert: URL-Ableitung + Hash-only).
Author
Owner

NICHT umgesetzt — bewusst. TLS-Zwang würde die aktuelle Prod-Instanz sofort lahmlegen: alle Agents laufen über ws:// (http SERVER_URL). Hartes Erzwingen = Disconnect aller Hosts. Self-Update-Signatur = Schlüssel-/Signier-Infra (Keygen, Signing, Agent-Verify). Braucht Deployment-Entscheidung (TLS-Rollout-Plan) + eigene Session. Vorschlag: optionales ENFORCE_TLS-Flag (default aus) + Warn-Log statt Hard-Break, Signatur separat.

NICHT umgesetzt — bewusst. TLS-Zwang würde die aktuelle Prod-Instanz sofort lahmlegen: alle Agents laufen über ws:// (http SERVER_URL). Hartes Erzwingen = Disconnect aller Hosts. Self-Update-Signatur = Schlüssel-/Signier-Infra (Keygen, Signing, Agent-Verify). Braucht Deployment-Entscheidung (TLS-Rollout-Plan) + eigene Session. Vorschlag: optionales ENFORCE_TLS-Flag (default aus) + Warn-Log statt Hard-Break, Signatur separat.
Author
Owner

Scope-Klarstellung: DEV-Umgebung, sauber statt kompatibel

Entscheidung: Dies ist eine reine Dev-Umgebung. Umsetzung clean, ohne Rueckwaertskompatibilitaet:

  • Keine Legacy-Fallbacks "fuer alte Nodes".
  • Verbindungsverlust bestehender Nodes ist akzeptiert (werden neu ausgerollt).
  • Alte Tokens/Verbindungen duerfen invalidiert werden.

Konkret fuer dieses Issue:

  • Phase 1 (wss): KEIN Insecure-Override (kein AGENT_ALLOW_INSECURE_WS o.ae.). ws:// zu Nicht-localhost = ausnahmsloser Startfehler. (ws://localhost/127.0.0.1/::1 bleibt fuer lokale Dev erlaubt.)
  • Phase 2 (Self-Update-Signatur): Update wird immer verweigert ohne gueltige ed25519-Signatur — kein Hash-Fallback, kein "Sig-Header fehlt → akzeptieren". Bestehende unsignierte Agents duerfen zurueckbleiben (neu ausrollen).
## Scope-Klarstellung: DEV-Umgebung, sauber statt kompatibel Entscheidung: Dies ist eine **reine Dev-Umgebung**. Umsetzung **clean, ohne Rueckwaertskompatibilitaet**: - **Keine Legacy-Fallbacks** "fuer alte Nodes". - **Verbindungsverlust bestehender Nodes ist akzeptiert** (werden neu ausgerollt). - Alte Tokens/Verbindungen duerfen invalidiert werden. Konkret fuer dieses Issue: - **Phase 1 (wss):** KEIN Insecure-Override (kein `AGENT_ALLOW_INSECURE_WS` o.ae.). `ws://` zu Nicht-localhost = **ausnahmsloser** Startfehler. (`ws://localhost/127.0.0.1/::1` bleibt fuer lokale Dev erlaubt.) - **Phase 2 (Self-Update-Signatur):** Update wird **immer** verweigert ohne gueltige ed25519-Signatur — **kein** Hash-Fallback, **kein** "Sig-Header fehlt → akzeptieren". Bestehende unsignierte Agents duerfen zurueckbleiben (neu ausrollen).
Author
Owner

#113 — beide Phasen umgesetzt (Issue bleibt offen)

Phase 1 — wss:// erzwingen · Commit 34a6176

  • config::validate_server_url: localhost/127.0.0.1/::1 → ws:// + wss:// (Dev); jeder andere Host → nur wss://, ws:// = harter Startfehler, kein Insecure-Override.
  • ws.rs: connect_async + rustls-webpki-roots verifiziert Server-Cert; kein danger_accept_invalid_certs (geprüft: keine Insecure-Pfade im Agent).
  • Backend install_script: SERVER_URL nicht https & Host nicht localhost → 400 statt ws://-Install-Script.
  • Akzeptanz: ws:// startet NICHT, wss:// startet, localhost weiter ws:// nutzbar, Install-Abruf bei nicht-https abgelehnt.

Phase 2 — ed25519-signiertes Self-Update · Commit eb9cde9

  • self_update.rs: verifiziert X-Binary-Sig (base64 ed25519) gegen eincompilierten UPDATE_PUBKEY_B64 (ed25519-dalek). Fehlt/ungültig → kein Ersetzen, alte Binary bleibt. Kein Hash-Fallback.
  • Backend node_agent_binary: liefert <binary>.sig als X-Binary-Sig (Build-/Release-Zeit, nicht per Request).
  • Tooling: scripts/gen-update-key.sh, scripts/sign-release.sh, wiki/Self-Update-Signing.md. Signing-Key = Offline-Secret (.gitignore *.pem).

⚠️ Setup nötig (Sebastian)

  1. scripts/gen-update-key.sh ausführen → Private Key (update-signing-key.pem) offline halten.
  2. Ausgegebenen Public Key in Node-Agent/src/self_update.rsUPDATE_PUBKEY_B64 eintragen (Placeholder = fail-closed, kein Update bis ersetzt).
  3. Node-Agent lokal bauen (kein cargo im Container), dann scripts/sign-release.sh theprox-node-agent-x86_64.sig neben Binary committen (je arch).

Alte/unsignierte Agents bleiben bewusst zurück → neu ausrollen. Issue bleibt offen (Rest von Epic #184).

## #113 — beide Phasen umgesetzt (Issue bleibt offen) **Phase 1 — wss:// erzwingen** · Commit `34a6176` - `config::validate_server_url`: localhost/127.0.0.1/::1 → ws:// + wss:// (Dev); jeder andere Host → **nur wss://**, ws:// = harter Startfehler, kein Insecure-Override. - `ws.rs`: `connect_async` + rustls-webpki-roots verifiziert Server-Cert; kein `danger_accept_invalid_certs` (geprüft: keine Insecure-Pfade im Agent). - Backend `install_script`: SERVER_URL nicht https & Host nicht localhost → 400 statt ws://-Install-Script. - Akzeptanz: ws://<remote> startet NICHT, wss://<remote> startet, localhost weiter ws:// nutzbar, Install-Abruf bei nicht-https abgelehnt. ✅ **Phase 2 — ed25519-signiertes Self-Update** · Commit `eb9cde9` - `self_update.rs`: verifiziert `X-Binary-Sig` (base64 ed25519) gegen eincompilierten `UPDATE_PUBKEY_B64` (ed25519-dalek). Fehlt/ungültig → kein Ersetzen, alte Binary bleibt. Kein Hash-Fallback. ✅ - Backend `node_agent_binary`: liefert `<binary>.sig` als `X-Binary-Sig` (Build-/Release-Zeit, nicht per Request). - Tooling: `scripts/gen-update-key.sh`, `scripts/sign-release.sh`, `wiki/Self-Update-Signing.md`. Signing-Key = Offline-Secret (`.gitignore *.pem`). ### ⚠️ Setup nötig (Sebastian) 1. `scripts/gen-update-key.sh` ausführen → Private Key (`update-signing-key.pem`) **offline** halten. 2. Ausgegebenen Public Key in `Node-Agent/src/self_update.rs` → `UPDATE_PUBKEY_B64` eintragen (Placeholder = fail-closed, kein Update bis ersetzt). 3. Node-Agent lokal bauen (kein cargo im Container), dann `scripts/sign-release.sh theprox-node-agent-x86_64` → `.sig` neben Binary committen (je arch). Alte/unsignierte Agents bleiben bewusst zurück → neu ausrollen. Issue bleibt offen (Rest von Epic #184).
Author
Owner

Verifiziert @ eb9cde9 → beide Phasen erfüllt, wird geschlossen.

Phase 1 (wss-Zwang) 34a6176:

  • validate_server_url in Config::load() aufgerufen (config.rs:35). wss→ok, ws→nur localhost/127.0.0.1/[::1], sonst harter Startfehler. Kein Insecure-Override ✓
  • Backend lehnt Install-Script bei nicht-https + non-localhost mit HTTP 400 ab (main.py:323) ✓
  • Keine accept-invalid-cert-Pfade ✓

Phase 2 (Self-Update-Signatur) eb9cde9:

  • Agent verifiziert X-Binary-Sig per ed25519 (ed25519-dalek), fail-closed — kein Hash-Fallback, fehlende/ungültige Signatur verweigert das Update (self_update.rs) ✓
  • Backend liefert Signatur aus .sig-Datei (Release-Zeit, main.py:356) ✓
  • scripts/sign-release.sh vorhanden ✓

⚠️ Offenes Action-Item (kein Code-Mangel — bewusst fail-closed)

UPDATE_PUBKEY_B64 in self_update.rs:33 ist noch der Platzhalter REPLACE_WITH_ED25519_PUBLIC_KEY_BASE64. Solange der drinsteht, wird jedes Self-Update abgelehnt. Vor produktivem Self-Update nötig:

  1. ed25519-Keypair via scripts/sign-release.sh erzeugen, Signing-Key offline halten.
  2. Public Key in self_update.rs:33 eintragen, Agent neu bauen.
  3. Bei jedem Release das Binary signieren → .sig neben das Binary legen.

In der Dev-Umgebung unkritisch (Neu-Ausrollung statt Self-Update).

**Verifiziert @ eb9cde9 → beide Phasen erfüllt, wird geschlossen.** **Phase 1 (wss-Zwang)** `34a6176`: - `validate_server_url` in `Config::load()` aufgerufen (config.rs:35). wss→ok, ws→nur localhost/127.0.0.1/[::1], sonst harter Startfehler. Kein Insecure-Override ✓ - Backend lehnt Install-Script bei nicht-https + non-localhost mit HTTP 400 ab (main.py:323) ✓ - Keine accept-invalid-cert-Pfade ✓ **Phase 2 (Self-Update-Signatur)** `eb9cde9`: - Agent verifiziert `X-Binary-Sig` per ed25519 (ed25519-dalek), fail-closed — kein Hash-Fallback, fehlende/ungültige Signatur verweigert das Update (self_update.rs) ✓ - Backend liefert Signatur aus `.sig`-Datei (Release-Zeit, main.py:356) ✓ - `scripts/sign-release.sh` vorhanden ✓ ## ⚠️ Offenes Action-Item (kein Code-Mangel — bewusst fail-closed) `UPDATE_PUBKEY_B64` in `self_update.rs:33` ist noch der Platzhalter `REPLACE_WITH_ED25519_PUBLIC_KEY_BASE64`. **Solange der drinsteht, wird jedes Self-Update abgelehnt.** Vor produktivem Self-Update nötig: 1. ed25519-Keypair via `scripts/sign-release.sh` erzeugen, **Signing-Key offline** halten. 2. **Public Key** in `self_update.rs:33` eintragen, Agent neu bauen. 3. Bei jedem Release das Binary signieren → `.sig` neben das Binary legen. In der Dev-Umgebung unkritisch (Neu-Ausrollung statt Self-Update).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
chinux/theProx#113
No description provided.