Table of Contents
- Public-Deployment + Hardening (ohne VPN)
- Architektur public
- 1. DNS + Hostnamen
- 2. Agent-Adressen: public vs VPN (#189)
- 3. NPM — Proxy-Host für den Agent-WS
- 4. Ports — was NICHT öffentlich darf
- 5. fail2ban für Agent-WS-Auth-Fails
- 6. Self-Update — TLS-Vertrauen, kein Key (#187)
- 7. Koordinierter Rollout bestehender Nodes (Auth-Umstellung)
- 8. Hardening-Checkliste
- 9. Rechtliches (kein Rechtsrat)
Public-Deployment + Hardening (ohne VPN)
Anleitung für den öffentlichen Betrieb: Backend hinter Nginx Proxy Manager
(NPM) + acme.sh-Zertifikat, Node-Agents binden per wss:// an die Domain — kein
VPN nötig. Voraussetzung sind die Security-Maßnahmen aus dem Public-Readiness-Epic
(#184): wss-Zwang (public) bzw. TRUSTED_TRANSPORT=vpn (VPN, #189), sha256-
geprüftes Self-Update (#187), interne Listener-Binds (#117/#180), ed25519-Auth
(#185/#182), Auth-Rate-Limit (#181).
Für den internen/VPN-Betrieb siehe Deployment. Diese Seite ergänzt sie um alles, was für „direkt aus dem Internet erreichbar" nötig ist.
Architektur public
Internet
│ :443 (TLS)
┌──────▼───────┐
│ Nginx Proxy │ acme.sh-Zertifikat, WebSocket-Support,
│ Manager │ Security-Header
└──────┬───────┘
│ intern (http/ws, nicht public)
┌──────▼───────┐
│ theProx │ :8765 Agent-WS · :8080 WebUI
│ Backend │
└──────────────┘
▲ wss://agent.example.com/ws/agent
│
Proxmox-Host (Node-Agent, root) — ohne VPN
Nur Port 443 (NPM) ist öffentlich. Alles andere bleibt host-/netz-intern.
1. DNS + Hostnamen
Empfehlung: Agent-WS auf eigenem Hostnamen/Pfad, getrennt von der UI — so kann die UI z. B. per IP-Allowlist/VPN geschützt bleiben, während der Agent-Endpunkt public erreichbar ist.
| Zweck | Beispiel-Host | Backend-Ziel | Public? |
|---|---|---|---|
| Agent-WebSocket | agent.example.com |
backend:8765 |
ja (443) |
| WebUI | theprox.example.com |
backend:8080 |
optional (besser intern/VPN/Allowlist) |
A-Record(s) auf die NPM-Public-IP zeigen lassen.
2. Agent-Adressen: public vs VPN (#189)
In server/.env gibt es zwei getrennte Agent-Adressen:
# Öffentlich erreichbare https-Adresse (Default = SERVER_URL, Legacy/Fallback).
SERVER_URL_PUBLIC=https://agent.example.com
# Interne VPN-Adresse (http erlaubt, weil VPN-gesichert). Globaler Default für
# Nodes mit aktiviertem VPN-Transport ohne Pro-Node-Override. Leer lassen, wenn
# es keine VPN-Nodes gibt.
SERVER_URL_VPN=http://172.16.1.10:8765
SERVER_URL bleibt als Legacy/Fallback für den Public-Wert gültig (ist
SERVER_URL_PUBLIC ungesetzt, gilt SERVER_URL).
Public-Nodes (Toggle aus, Default):
- Basis =
SERVER_URL_PUBLIC. Darauswss://…/ws/agent+https://…. - Niemals
http://für public. Für einen Nicht-https-, Nicht-localhost-Wert liefert das Backend bewusst kein Install-Script aus (400, #113 Phase 1), und der Agent verweigertws://zu einem Nicht-localhost-Host beim Start.
VPN-Nodes (Toggle „Verbindung über VPN")
Für Nodes, die das Backend nur über ein VPN erreichen, ist kein öffentliches Zertifikat nötig — der Transport ist durch das VPN gesichert.
- Toggle pro Node im Anlegen-Dialog und in den Node-Einstellungen
(
vpn_transport); optional eine Pro-Node-VPN-Adresse (vpn_server_url), die den globalenSERVER_URL_VPNschlägt. - Das Install-Script liefert dann
ws://…/ws/agent+http://…und schreibtTRUSTED_TRANSPORT=vpninsagent.conf. Genau dieser Schlüssel hebt im Agent den Klartext-Zwang auf (ws:///http://erlaubt) — ohne ihn bleibt der scharfewss:///https://-Zwang aktiv. - Kein stilles Fallback: Toggle an, aber weder
vpn_server_urlnochSERVER_URL_VPNgesetzt → 400, es wird nicht still die Public-Adresse ausgeliefert. - sha256 bleibt: auch über VPN prüft der Agent das Self-Update gegen
X-Binary-Hash(Integrität), siehe Self-Update-Signing. - Wirksamkeit: ein geänderter Toggle greift erst beim erneuten Ausrollen des Agents (Install-Script erneut ausführen), nicht zur Laufzeit.
Nach Änderung Backend neu bauen:
cd server && docker compose up -d --build
3. NPM — Proxy-Host für den Agent-WS
Proxy Hosts → Add Proxy Host
- Domain Names:
agent.example.com - Scheme:
http· Forward Hostname/IP:backend(bzw. Backend-Host) · Forward Port:8765 - Websockets Support: AN ← zwingend für
/ws/agent, sonst kein Agent-Connect. - Block Common Exploits: an.
SSL-Tab
- Zertifikat per Let's Encrypt (NPM nutzt acme.sh) ausstellen, Force SSL, HTTP/2, HSTS Enabled (+ Subdomains, sofern passend).
Advanced-Tab (Security-Header + großzügige WS-Timeouts):
# Security-Header
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "no-referrer" always;
# WebSocket: lange Verbindung am Leben halten (Agent-Keepalive ~30s)
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
# Echte Client-IP an das Backend (für Rate-Limit + fail2ban, #181)
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Die
X-Real-IP/X-Forwarded-For-Weitergabe ist wichtig: ohne sie sieht das Backend nur die NPM/Docker-Bridge-IP, und das Auth-Rate-Limit (#181) würde alle Agents als „eine IP" behandeln.
Für die WebUI (theprox.example.com) analog, aber WebSocket-Support nur falls
benötigt; UI-Host idealerweise zusätzlich per Access-List/Allowlist schützen.
4. Ports — was NICHT öffentlich darf
Nur 443 (NPM) gehört ins Internet. Alles andere intern halten (Firewall /
Docker-Bind an 127.0.0.1 / privates Netz):
| Port | Dienst | Sichtbarkeit |
|---|---|---|
| 443 | NPM (TLS) | public |
| 8765 | Agent-WebSocket | nur via NPM (nicht direkt) |
| 8080 | WebUI | intern / VPN / Allowlist |
| 5432 | PostgreSQL | niemals public — nur Backend-intern |
| 9100 | VM-Guest-Agent-Listener auf dem Proxmox-Host | host-intern, bindet per Default 127.0.0.1 bzw. internes Netz (#117) |
| 9101 | VM-Binary-HTTP auf dem Proxmox-Host | host-/netz-intern |
| random | RDP-/Tunnel-Listener auf dem Host | bindet auf interne Adresse, nie 0.0.0.0 (#180) |
Checks:
- Docker-Compose-
ports:für DB/Backend nicht auf0.0.0.0exponieren — Backend nur an NPM, DB nur intern. - Auf dem Proxmox-Host setzt der Agent die Bind-Adresse aus
INTERNAL_NETWORK/VM_LISTENER_BIND; ohne Konfig127.0.0.1. Host-Firewall: 9100/9101 nicht aus dem Internet erreichbar.
5. fail2ban für Agent-WS-Auth-Fails
Das Backend drosselt Auth-Fehlversuche pro IP bereits prozessintern (#181) und loggt fail2ban-parsebar. Filter/Jail + Log-Format sind in Security dokumentiert:
# /etc/fail2ban/filter.d/theprox-agent-auth.conf
[Definition]
failregex = ^.*agent-auth-fail ip=<HOST> reason=.*$
ignoreregex =
Damit <HOST> die echte Agent-IP ist, muss NPM X-Real-IP setzen (Schritt 3).
6. Self-Update — TLS-Vertrauen, kein Key (#187)
Das Self-Update braucht keinen Signing-Key mehr. Authentizität kommt aus dem
Transport (TLS bei public, VPN bei TRUSTED_TRANSPORT=vpn); die Integrität prüft
der Agent über den X-Binary-Hash-Header (sha256, fehlend/Mismatch → verweigert).
Es gibt keine .sig-Dateien und keinen eincompilierten Public Key mehr. Siehe
Self-Update-Signing.
7. Koordinierter Rollout bestehender Nodes (Auth-Umstellung)
⚠️ Wichtig bei der Umstellung auf #185 (ed25519-Auth). Der alte Bearer-Token-Pfad ist entfernt. Sobald das Backend mit der neuen Auth läuft, können sich bestehende Agents nicht mehr verbinden, bis sie ein neues Binary haben und neu enrollt sind. Die Nodes gehen also vorübergehend offline — das ist erwartet. Reihenfolge am Stück durchziehen:
-
Backend neu bauen + migrieren
cd server && docker compose up -d --build docker compose exec <backend> alembic upgrade head # Migration 0024(
cryptography==44.0.0ist inrequirements.txtgepinnt.) -
Node-Agent bauen + ausrollen — der Agent wird lokal gebaut (kein cargo im Container) und das neue Binary auf die Proxmox-Hosts verteilt. Ein altes Binary gegen ein neues Backend = Node bleibt offline.
-
Pro Node neu enrollen — frischen single-use Enroll-Token erzeugen:
POST /api/admin/nodes/{node_name}/regenerate-tokenDer Agent registriert beim ersten Connect seinen selbst erzeugten ed25519-Public-Key (TTL + single-use, #185/#182). Danach läuft die Auth ausschließlich über Challenge-Response.
Ein Node sperren = seinen identity_pubkey löschen (betrifft nur diesen Node).
8. Hardening-Checkliste
SERVER_URL_PUBLIC=https://…gesetzt (niehttp://public); Backend neu gebaut. Bei VPN-Nodes zusätzlichSERVER_URL_VPN=http://…(oder Pro-Node-vpn_server_url) und Toggle „Verbindung über VPN" am Node (#189).- NPM-Proxy-Host für
agent.example.commit Websockets Support AN. - Gültiges acme.sh/Let's-Encrypt-Zertifikat, Force SSL, HSTS.
- Security-Header gesetzt (HSTS, nosniff, X-Frame-Options, Referrer-Policy).
X-Real-IP/X-Forwarded-Foran das Backend durchgereicht.- Nur 443 public; 8765/8080/5432 nicht direkt erreichbar.
- PostgreSQL nur backend-intern (kein
0.0.0.0-Port-Bind). - Proxmox-Host: 9100/9101 + Tunnel-Ports per Firewall intern; Agent-Bind via
INTERNAL_NETWORK/VM_LISTENER_BIND(oder Default127.0.0.1). - Public-Agent verbindet per
wss://(ws:// zu Remote-Host = Startfehler, #113). VPN-Agent perws://+TRUSTED_TRANSPORT=vpn(#189) — bewusst. - Nodes per ed25519-Identity enrollt (#185); Enroll-Token single-use + TTL.
- fail2ban-Filter/Jail aktiv (Security).
- Self-Update: kein Key nötig — sha256 über
X-Binary-Hash(#187). TLS bzw. VPN sichert die Authentizität (Self-Update-Signing). - WebUI-Host zusätzlich geschützt (VPN/Access-List), falls public erreichbar.
9. Rechtliches (kein Rechtsrat)
Wer theProx öffentlich/für Dritte anbietet, fällt potenziell unter Pflichten wie die EU Cyber Resilience Act (CRA) (Sicherheits-Updates, Schwachstellen- Handling, Doku) und Datenschutz (DSGVO). Das ist nur ein Hinweis, kein Rechtsrat — vor produktivem öffentlichen Angebot rechtlich prüfen lassen.