- Python 73.8%
- Vue 20.6%
- Shell 2.3%
- JavaScript 1.9%
- CSS 0.8%
- Other 0.5%
|
All checks were successful
CI / Backend (Python 3.11) (push) Successful in 1m3s
CI / Backend (Python 3.12) (push) Successful in 1m10s
CI / Sicherheitsprüfung der Abhängigkeiten (push) Successful in 24s
CI / Frontend-Build (push) Successful in 11s
CI / Shell-Skripte (push) Successful in 9s
|
||
|---|---|---|
| .github/workflows | ||
| backend | ||
| deploy | ||
| frontend-vue | ||
| migrations | ||
| mobile | ||
| scripts | ||
| tests | ||
| .env.example | ||
| .gitignore | ||
| alembic.ini | ||
| CONTRIBUTING.md | ||
| manage.py | ||
| pyproject.toml | ||
| README.md | ||
| requirements-dev.txt | ||
| requirements.txt | ||
| run.sh | ||
vServer Manager
Eine schlanke, web-basierte Steuerzentrale, um mehrere Linux-vServer über SSH zu verwalten — Housekeeping, Patch-Reporting, Docker, Dateien, Config- Validierung, Cron und eine eingebaute SSH-Konsole. Backend in FastAPI (Python), Frontend als Single-Page-App (Vanilla JS, keine Build-Kette).
⚠️ Bitte zuerst den Abschnitt Sicherheit lesen. Das Tool hält privilegierte Zugangsdaten zu deinen Servern. Niemals ungeschützt ins offene Internet stellen.
Hinweis zum Update auf diese Version
Alle bestehenden Anmeldungen werden einmalig ungültig. Jeder muss sich nach dem Update neu anmelden.
Grund: Tokens tragen jetzt eine Sitzungs-ID (jti), über die sich ein einzelnes
Gerät abmelden lässt. Ein Token ohne diese ID lässt sich keiner Sitzung
zuordnen — damit wäre nicht feststellbar, ob die Sitzung inzwischen widerrufen
wurde, und ein Logout bliebe wirkungslos. Solche Tokens gelten deshalb nicht
mehr.
Die native App muss neu gebaut werden. Der Terminal-WebSocket akzeptiert
nur noch Einmal-Tickets; der frühere Parameter ?token= ist entfernt. Ältere
App-Versionen können das Terminal sonst nicht mehr öffnen (der Rest der App
funktioniert weiter). Neu bauen mit ./scripts/build-frontend.sh und
mobile/sync-www.sh.
Schnellstart
cd vserver-manager
cp .env.example .env # und VSM_SECRET + Admin-Passwort anpassen
./scripts/build-frontend.sh # Oberfläche bauen -> frontend-dist/
./run.sh # legt venv an, installiert Deps, startet uvicorn
scripts/build-frontend.sh prüft, ob Node.js >= 20 vorhanden ist, und
installiert es bei Bedarf — auf Debian-basierten Systemen per apt bzw.
NodeSource, auf RHEL-basierten per dnf/Module-Stream bzw. NodeSource. Ohne
Installation läuft es mit --no-install, ohne Rückfrage mit --yes, und
--check prüft nur die Voraussetzungen. Wer Node schon passend hat, kann
genauso gut direkt (cd frontend-vue && npm ci && npm run build) aufrufen.
Der Build wird einmalig (und nach jeder Änderung unter frontend-vue/)
gebraucht; er erzeugt frontend-dist/, das der Server unter /static/
ausliefert. Zum Entwickeln am Frontend siehe
frontend-vue/README.md (npm run dev mit HMR).
Die Datenbank wird beim Start automatisch angelegt bzw. migriert (Alembic) —
eine bestehende Installation aus v8 wird dabei übernommen, ohne dass etwas von
Hand zu tun ist. Details und die Testsuite: CONTRIBUTING.md.
Dann im Browser: http://127.0.0.1:8000
Erstanmeldung mit den Werten aus VSM_ADMIN_USER / VSM_ADMIN_PASSWORD
(Default admin / admin — sofort ändern).
Manueller Start ohne run.sh:
./scripts/build-frontend.sh
python3 -m venv venv && . venv/bin/activate
pip install -r requirements.txt
uvicorn backend.main:app --host 127.0.0.1 --port 8000
Als Dienst (systemd)
Für den Dauerbetrieb liegt unter deploy/vserver-manager.service eine
gehärtete Unit (dedizierter Benutzer, ProtectSystem=strict, gefilterte
Syscalls, keine Capabilities, DB unter /var/lib/vserver-manager via
StateDirectory). Kurzfassung:
sudo cp -r vserver-manager /opt/ && cd /opt/vserver-manager
./scripts/build-frontend.sh --yes
python3 -m venv venv && ./venv/bin/pip install -r requirements.txt
sudo useradd --system --home /opt/vserver-manager --shell /usr/sbin/nologin vsm
sudo chown -R vsm:vsm /opt/vserver-manager
sudo -u vsm cp .env.example .env && sudo -u vsm chmod 600 .env # dann .env anpassen
sudo cp deploy/vserver-manager.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now vserver-manager
Die Unit lauscht bewusst nur auf 127.0.0.1:8000 (TLS/Reverse-Proxy davor) und
läuft mit genau einem Worker, da die OIDC-/WebAuthn-Zwischenspeicher im
Prozessspeicher liegen. Anpassbare Pfade/Benutzer stehen kommentiert in der
Datei.
Als TLS-Frontend liegt unter deploy/Caddyfile eine fertige Caddy-Konfiguration
bei: automatisches Let's-Encrypt-Zertifikat, WebSocket-Durchreichung fürs
Terminal und eine auf die App abgestimmte Content-Security-Policy. Nur Domain
und E-Mail eintragen, nach /etc/caddy/Caddyfile legen und systemctl reload caddy.
Architektur
Browser können nicht direkt SSH sprechen, daher gibt es ein echtes Backend, das die SSH-Verbindungen (via paramiko) hält und eine REST-/WebSocket-API anbietet. Das Frontend ist statisch und spricht ausschließlich mit diesem Backend.
Browser (SPA) ──HTTPS/REST──▶ FastAPI-Backend ──SSH/SFTP──▶ vServer 1..n
│ │
└────────WebSocket────────────┘ (interaktives Terminal)
| Bereich | Datei(en) |
|---|---|
| SSH-Kern | backend/ssh.py (connect/run/sudo/sftp) |
| Auth & 2FA | backend/security.py, routers/auth.py |
| OIDC / SSO | backend/oidc.py, routers/auth.py |
| Passkeys/FIDO2 | backend/webauthn_svc.py |
| Server-CRUD | routers/servers.py |
| System-Infos | backend/sysinfo.py, routers/servers.py |
| System/Patches | routers/system.py |
| Repositories | backend/repos.py, routers/system.py |
| Firewall | backend/firewall.py, routers/firewall.py |
| fail2ban | backend/fail2ban.py, routers/fail2ban.py |
| Docker | routers/docker.py |
| Dateien | routers/files.py |
| Config-Check | routers/validate.py |
| Config-Editor | routers/configfiles.py |
| Terminal | routers/terminal.py (WebSocket) |
| Cron-Tasks | routers/tasks.py, backend/scheduler.py |
| Protokoll | routers/audit.py (lesen), security.audit() (schreiben) |
| Frontend | frontend-vue/ (Vue 3 + Vite → frontend-dist/) |
| DB-Migrationen | backend/db_init.py, migrations/ (Alembic) |
| Tests / CI | tests/, .github/workflows/ci.yml |
Verbindung wahlweise per SSH-Key (Ed25519/RSA/ECDSA, optional Passphrase)
oder User/Passwort. sudo läuft über sudo -S mit Passwort-Eingabe per
stdin; das sudo-Passwort wird pro Server hinterlegt.
Features & Status
Ehrlich gehalten — was voll funktioniert und was bewusst als Gerüst / Erweiterungspunkt angelegt ist.
Voll implementiert
- Berechtigungen pro Server — globale Rolle superadmin (verwaltet alles: alle Server, Benutzer, Zeitpläne) und normale Benutzer, denen pro Server eine Rolle (admin/operator/viewer) zugewiesen wird. Ohne Zuweisung kein Zugriff; die Serverliste zeigt jedem nur die eigenen Server. Die Zuweisung erfolgt im Benutzer-Dialog (Superadmin) über eine Server-Rechte-Matrix; Änderungen wirken sofort und beenden laufende Sitzungen des Benutzers. Als viewer verbergen sich schreibende Tabs (Terminal, Dateien, Housekeeping) und alle schreibenden Aktionen in den übrigen Tabs, sodass nur eine Nur-Lese-Oberfläche bleibt.
- Aktionsprotokoll — jede ausgeführte Aktion wird mit Zeitpunkt, Benutzer, Aktion und betroffenem Server festgehalten und ist im Header unter Protokoll einsehbar (nur für Superadmins, Button wird anderen gar nicht erst eingeblendet und der Endpunkt antwortet mit 403). Zeitstempel kommen als UTC mit Zeitzonenangabe und werden im Browser in die lokale Zeit umgerechnet. Filterbar nach Benutzer, Server, Freitext und Fehlschlägen; das Protokoll ist über die Oberfläche nur lesbar, es gibt keine Lösch- oder Änderungsfunktion. Protokolliert werden u. a. An-/Abmeldungen (auch fehlgeschlagene), Änderungen an Servern, Benutzern und Rechten, Housekeeping, Repository-, Firewall-, fail2ban-, Docker- und Datei-Operationen, Prozess-Eingriffe, Zeitpläne sowie Öffnen und Schließen einer SSH-Terminal-Sitzung. Der Servername wird denormalisiert mitgeschrieben, sodass Einträge auch nach dem Löschen eines Servers lesbar bleiben (dann als „gelöscht" markiert). Export als PDF (Querformat, mit Filterangaben im Kopf) oder CSV (Semikolon + BOM, öffnet in Excel ohne Import-Assistent; enthält zusätzlich eine UTC-Spalte und den technischen Aktions-Code). Der Export-Dialog filtert nach Benutzer, Server, Zeitraum, Suchbegriff und Fehlschlägen und übernimmt die Filter der Ansicht als Vorbelegung; begrenzt auf 20 000 Zeilen pro Datei. Bei Benutzern und Servern ist Mehrfachauswahl möglich (leere Auswahl = alle); die Kombination wirkt als UND, also „diese Benutzer auf diesen Servern". Leeren des Protokolls ist nach Rückfrage möglich – der Vorgang schreibt anschließend selbst einen Eintrag, wer wann wie viele Einträge gelöscht hat, denn ein spurlos leeres Protokoll wäre genau das, was ein Angreifer hinterlassen würde.
- Designs — drei umschaltbare Oberflächen-Themes (Classic, Bootstrap Light, Bootstrap Dark), pro Benutzer gespeichert und im Header wählbar.
- System-Übersicht — lädt direkt CPU (Modell, Kerne, Load, aktuelle Auslastung), RAM und Storage je mit Belegung, Netzwerk-Karten samt Konfiguration (MAC, IPv4/IPv6, Gateway, MTU), OS-Name/-Version, Kernel und Paketanzahl — plus Knopf für die vollständige Paketliste (mit Versionen, filterbar). Alles in einem SSH-Aufruf.
- Server-Verwaltung inkl. Verbindungstest und automatischer OS-Erkennung (Debian/Ubuntu, RHEL/Fedora, NixOS).
- Housekeeping — OS-Updates, Paket-Cache & Temp/Journal-Bereinigung, pro OS-Familie passende Befehlssätze.
- Patch-Reporting — verfügbare Updates auflisten (apt/dnf/nixos).
- Firewall — erkennt selbstständig ufw oder firewalld, zeigt die aktuellen Regeln (ufw nummeriert; firewalld nach Services/Ports/Rich-Rules mit Zone) und erlaubt Hinzufügen (Vorlagen für gängige Dienste oder eigene Regel) und Entfernen. Bei firewalld wird nach Änderungen automatisch neu geladen. Fehlt eine Firewall, lässt sie sich per Klick installieren – der SSH-Port der aktuellen Verbindung wird dabei vor der Aktivierung freigegeben, damit sich vServer Manager nicht selbst aussperrt.
- fail2ban — Status und Jails (mit aktuell/gesamt fehlgeschlagenen und
gebannten Zählern), gebannte IPs je Jail einsehen, IPs bannen/entbannen sowie
jail.localbearbeiten (mit Backup, Konfigurationstest und reload). - Repository-Management — strukturierte Übersicht aller Quellen mit
Distro-Komfort: Debian/Ubuntu PPAs (
add-apt-repository) sowie Repo + GPG-Key per modernersigned-by/keyrings-Methode; RHEL/Fedora COPR (dnf copr), Repo per.repo-URL (config-manager) undrpm --import; NixOS-Channels. Quellen lassen sich aktivieren/deaktivieren und entfernen, plus Index-Aktualisierung. Häufige Repositories per Klick (Docker CE, Tailscale, HashiCorp, NodeSource, PostgreSQL-PGDG, EPEL) werden versionsgenau eingerichtet – der Katalog erkennt Distribution, Version/Codename und Architektur automatisch. - SSH-Key Enrollment & Revocation — Keys in
authorized_keyseines Zielbenutzers eintragen/entfernen. - Docker — Container auflisten, neu starten via
docker run(Image, Ports, Volumes, Env, Restart-Policy, optionaler Pull), Start/Stop/Restart/ Remove, Logs; Compose-Projekte auflisten, up/down/restart/pull sowie neue Projekte anlegen und bestehende bearbeiten (compose.yml schreiben/editieren undup -d, jeweils mit.vsm-bak-Backup). Zum Aufräumen lassen sich ungenutzte Volumes per Klick löschen (docker volume prune -a). Mehrere Container lassen sich per Checkbox auswählen und gebündelt entfernen – wahlweise inklusive ihrer benannten Volumes und Images. Dasselbe gilt für Compose-Projekte (herunterfahren und entfernen, wahlweise inkl. Volumes/Images; verwaltete Projektverzeichnisse werden dabei gelöscht). Fehlt Docker, lässt es sich per Klick installieren (offiziellesget.docker.com-Skript inkl. Compose-Plugin, Dienst wird aktiviert). Von VSM angelegte Compose-Projekte bleiben nachdownin der Liste sichtbar (als „verwaltet" markiert) und lassen sich wieder hochfahren. - Datei-Browser — Navigieren, Lesen/Bearbeiten, Up-/Download, neue Ordner und neue Dateien anlegen, rename, copy, move, delete (mit Schutz vor Löschen kritischer Pfade).
- Config-Editor — distro-abhängiger Schnellzugriff auf gängige System-
Configs (sshd, fstab, netplan, dnf/apt, NixOS u. a.) per Dropdown: laden,
bearbeiten, speichern (per
sudo, mit automatischem.vsm-bak-Backup) oder beliebiger Pfad. Plus Syntax-Validierung — JSON, YAML, TOML, INI lokal und .nix übernix-instantiateauf dem Zielserver (mit Klammer-Fallback). - SSH-Konsole — voll interaktives Terminal (xterm.js) im Tab oder in
eigenem Browser-Tab (
term.html). - Geplante Tasks — Cron-Ausdrücke, persistente Tasks via APScheduler, manueller Sofort-Lauf; Aktionen: Housekeeping, Patch-Report, Freier Befehl. Ein Task kann mehrere Server als Ziel haben und läuft dann auf allen; der Gesamtstatus ist nur „ok", wenn alle erfolgreich waren.
- Auth — lokaler Benutzer mit Rollen (admin/operator/viewer), TOTP-2FA
(Authenticator-App, QR-Setup), WebAuthn/FIDO2-Passkeys & Security-Keys
(passwortlose Anmeldung) sowie OIDC/SSO-Login mit voller ID-Token-
Prüfung (JWKS-Signatur,
iss/aud/exp/iat,nonce-Replay-Schutz). - Zentrale SSH-Schlüsselverwaltung — vserver-manager erzeugt ein eigenes Ed25519-Paar; der private Teil liegt einmal verschlüsselt in der Datenbank, die Server verweisen darauf. Eine Rotation ist damit eine Änderung an einer Stelle statt an jedem Server. Der Ablauf ist bewusst mehrstufig: erzeugen → ausrollen → prüfen → umstellen → zurückziehen. Das Umstellen verlangt je Server einen bestandenen Verbindungstest, das Zurückziehen verweigert sich, solange noch Server den Schlüssel benutzen.
- Sicherung, Wiederherstellung und Secret-Rotation —
./manage.py backup,restore,rotate-secret. Die Rotation entschlüsselt erst alles im Speicher und bricht bei einem einzigen unlesbaren Wert ab, bevor etwas geschrieben wird; ein Fingerabdruck verhindert das Zurückspielen mit falschem Secret. - Sudoers-Generator — erzeugt aus den tatsächlich verwendeten Befehlen ein
/etc/sudoers.d/vsm-Snippet, passend zur Distribution, und installiert es übervisudo -c(erst prüfen, dann verschieben). Deutlich enger alsNOPASSWD: ALL, aber ausdrücklich keine Sandbox. - Stummschalten von Warnungen — „ist bekannt, Ruhe bis nächste Woche", je Befund und Server, in der Datenbank und damit über Neustarts hinweg.
- Selbstdiagnose je Server — prüft in einem Durchgang die bekannten
Stolperstellen (SSH-Anmeldung, Host-Key, sudo inklusive Antwortzeit,
Schlüsselverwaltung, systemd/journalctl/Docker, Plattenplatz, Rechte auf
~/.ssh) und sagt zu jedem Befund, was zu tun ist. Adressiert die Klasse von Problemen, die sich sonst als „geht irgendwie nicht" äußert. Auch über die gesamte Flotte auf einmal — „auf welchem meiner Server klemmt etwas?" ist die Frage, die im Alltag zuerst kommt — allen voran falsche sudo-Zugangsdaten, die jeden Befehl um rund zwei Sekunden verzögern, ohne dass irgendwo ein Fehler erscheint. - Tags als Gruppen — Server lassen sich taggen und danach filtern
(Sidebar, Flotten-Übersicht). Zeitpläne können statt fester Server-IDs auf
Tags zielen: „alle mit Tag
web" nimmt später angelegte Server automatisch mit. Tags werden normalisiert gespeichert, damitweb,Webundwebnicht zu drei Gruppen werden. - Trend-Warnungen — „Platte voll in etwa drei Tagen" statt „Platte bei 91 %". Lineare Regression über die Metrik-Historie; die Warnung kommt, bevor der Schwellwert erreicht ist.
- Periodische Selbstdiagnose — die Flotten-Diagnose läuft auf Wunsch von
selbst und meldet neue Fehler. Nur
fail, nicht die Einrichtungshinweise. - Automatische Sicherung — als Zeitplan, mit Aufbewahrungsfrist. Eine gescheiterte Sicherung wird gemeldet; sie ist genau die Sorte Problem, die man sonst erst im Ernstfall bemerkt.
- API-Tokens — langlebiger, widerrufbarer Nur-Lese-Zugang für Monitoring, optional auf einzelne Server begrenzt. Gespeichert wird nur der Hash.
- Zertifikate — TLS-Zertifikate von certbot und von Hand abgelegte in einer
Liste. Ausstellen, erneuern, zurückziehen, Probelauf (
--dry-run) und ein eigener Hinweis, wenn der Erneuerungs-Timer nicht läuft — die häufigste Ursache dafür, dass ein Zertifikat „plötzlich" abläuft. - Webserver — nginx/Apache mit Bestandsaufnahme, vHosts und einem 1-Klick-Stack-Installer. Die Auswahl kommt vom Zielsystem: Es wird erst nachgesehen, welche PHP-Versionen und Datenbanken die Distribution überhaupt anbietet. Reload nur nach bestandener Konfigurationsprüfung.
- PostgreSQL — Datenbanken und Rollen anlegen, löschen, Rechte vergeben.
Über
sudo -u postgresauf dem Zielserver, also ohne hinterlegte Zugangsdaten und ohne offenen Port. Systemobjekte sind geschützt. - CPU-Topologie — Kerne, Threads und Sockel getrennt ausgewiesen. Bei aktivem SMT zeigt die Übersicht „4 / 8"; ist die Topologie nicht ermittelbar (ARM, Container, viele Hypervisoren), nur eine Zahl statt einer erfundenen Aufteilung.
- Metrik-Historie — Verlauf von CPU, RAM, Plattenbelegung und Load je
Server, als Diagramm in der Übersicht. Drei Tage in voller Auflösung, danach
bis Tag sieben auf Stundenwerte verdichtet (Mittel- und Maximalwert, damit
Spitzen erhalten bleiben), älteres wird gelöscht. Einstellbar über
VSM_METRIC_RETENTION_PERIODundVSM_METRIC_RAW_PERIOD. - Flotten-Dashboard — alle Server auf einen Blick: Erreichbarkeit, CPU, RAM, vollste Platte, offene Updates, Container, Uptime. Sortiert nach Dringlichkeit. Die Daten kommen aus einer periodischen Hintergrund-Erfassung; beim Öffnen wird keine SSH-Verbindung aufgebaut.
- Hintergrund-Vorgänge mit Live-Ausgabe — Housekeeping und manuell
gestartete Zeitpläne laufen als Job und liefern ihre Ausgabe zeilenweise über
Server-Sent Events. Der Verlauf bleibt in
job_runserhalten. - Benachrichtigungen — Webhook oder ntfy bei nicht erreichbaren Servern, volllaufenden Platten, nötigen Neustarts und fehlgeschlagenen Zeitplänen. Mit Entprellung, damit derselbe Zustand nicht dauerhaft meldet.
- systemd-Dienste — Units auflisten, filtern (auch „nur fehlgeschlagene"), starten/stoppen/neu starten, Autostart umschalten und das Journal einsehen.
- Wiederherstellungscodes — bei der TOTP-Aktivierung werden zehn Einmal-Codes erzeugt und genau einmal angezeigt. Jeder ersetzt den 2FA-Code beim Anmelden. Ohne sie sperrt sich aus, wer sein Telefon verliert und keinen Passkey hat. Gespeichert wird nur der bcrypt-Hash.
- Sitzungsverwaltung — jede Anmeldung ist einzeln sichtbar (Gerät, IP, letzte Aktivität) und einzeln beendbar. Ein Logout beendet nur diese Anmeldung; ein Passwortwechsel beendet alle anderen. Deaktivierung und Rechteentzug beenden weiterhin alle.
- Verschlüsselung at rest — alle Server-Credentials werden mit Fernet
(Schlüssel aus
VSM_SECRETabgeleitet) verschlüsselt in der DB abgelegt. - Audit-Log — sicherheitsrelevante Aktionen werden protokolliert.
Gerüst / bewusste Erweiterungspunkte
- Mehrere Worker: Der Scheduler läuft im Prozess. Bei
uvicorn --workers Nführt sonst jeder Worker die geplanten Aufgaben aus — genau einen Prozess mitVSM_SCHEDULER=truebetreiben, alle übrigen mitfalse. - Skalierung: SQLite + In-Process-Scheduler sind für Einzelinstanz gedacht.
Für HA: Postgres (
VSM_DATABASE_URL) und externen Scheduler/Worker. Die OIDC-State-/JWKS-Caches liegen im Prozessspeicher; für Multi-Worker einen geteilten Store (z. B. Redis) nutzen. - SSH-Verbindungen laufen über einen Pool (
backend/sshpool.py): je Server eine offene Verbindung, über die mehrere Befehle gleichzeitig multiplext werden. Gemessen gegen einen echten sshd: 20 Befehle erzeugen eine statt 20 Anmeldungen in dessenauth.log. Abschaltbar überVSM_SSH_POOL=false. Die Verbindung wird verworfen, sobald sich Zugangsdaten oder Host-Key ändern.
Sicherheit
Dieses Tool ist eine privilegierte Admin-Konsole. Behandele es wie deinen SSH-Sprunghost:
VSM_SECRETwird automatisch generiert und in<DATA_DIR>/secret.key(0600) abgelegt, falls nicht gesetzt. Für Multi-Host/HA explizit setzen und getrennt von der Datenbank aufbewahren. Aus diesem Secret wird – per HKDF und getrennt vom JWT-Signierschlüssel – der Verschlüsselungsschlüssel für alle gespeicherten Server-Credentials abgeleitet. Der frühere unsichere Beispielwert wird beim Start abgelehnt.- Admin-Passwort: ist
VSM_ADMIN_PASSWORDnicht gesetzt, wird beim ersten Start ein zufälliges erzeugt und einmalig ins Log geschrieben. Danach in der App unter „Sicherheit“ änderbar. - Niemals ohne TLS und ohne Zugriffsschutz exponieren. Empfohlen:
- Reverse Proxy (nginx/Caddy/Traefik) mit HTTPS davor.
- Erreichbarkeit auf VPN/Tailscale/WireGuard oder ein internes Netz beschränken.
- 2FA für alle Konten aktivieren.
- Least Privilege: je Server einen dedizierten Wartungsbenutzer mit nur den nötigen sudo-Rechten statt root.
- WebSocket-Terminal authentifiziert per Token im Query-String (Browser senden bei WS keine Header) — hinter TLS unkritisch, ohne TLS landet das Token im Klartext. Also: TLS.
Umgesetzte Härtungen: SSH-Host-Key-Pinning (TOFU; MITM-Schutz, in der
Server-Ansicht zurücksetzbar) · Rollen greifen auch für Datei-Zugriff und
Terminal (nur admin/operator) · Login-Rate-Limiting · Token-Widerruf über
Logout/Passwortänderung/Deaktivierung (token_version) · konservatives
OIDC-Provisioning (Default viewer, optionale Domain-Allowlist) ·
Security-Header (CSP/HSTS/X-Frame-Options …) auch ohne Proxy · Benutzer
deaktivieren/löschen mit Schutz des letzten Admins · 2FA-Deaktivierung nur mit
Passwortbestätigung. Bewusst belassene Trade-offs: das JWT liegt clientseitig in
sessionStorage (per CSP flankiert), das Terminal-Token im Query-String.
Konfiguration (Auszug)
Alles über Umgebungsvariablen / .env — Details in .env.example.
| Variable | Zweck |
|---|---|
VSM_SECRET |
Verschlüsselung + JWT-Signatur (Pflicht) |
VSM_ADMIN_USER/PASSWORD |
Bootstrap-Admin beim ersten Start |
VSM_DATABASE_URL |
DB-DSN (Default SQLite) |
VSM_TOKEN_TTL_MIN |
Session-Laufzeit in Minuten (Default 480) |
VSM_SSH_TIMEOUT |
SSH-Connect-Timeout |
VSM_SSH_CMD_TIMEOUT |
Befehls-Timeout |
VSM_WEBAUTHN_RP_ID |
FIDO2 Relying-Party-ID (Domain ohne Schema) |
VSM_WEBAUTHN_ORIGIN |
Erlaubte Origin(s) mit Schema/Port, kommagetr. |
VSM_OIDC_* |
SSO-Konfiguration (siehe unten) |
Passkeys / FIDO2 einrichten
WebAuthn ist out-of-the-box aktiv (kein externer Dienst nötig). Wichtig sind nur zwei Werte, die zur aufgerufenen URL passen müssen:
# lokal
VSM_WEBAUTHN_RP_ID=localhost
VSM_WEBAUTHN_ORIGIN=http://localhost:8000
# Produktion (Beispiel)
VSM_WEBAUTHN_RP_ID=vsm.example.com
VSM_WEBAUTHN_ORIGIN=https://vsm.example.com
RP_ID ist die Domain ohne Schema/Port, ORIGIN die vollständige URL. Bei
Abweichung verweigert der Browser die Passkey-Erstellung. Ein registrierter
Passkey erlaubt passwortlosen Login über „Mit Passkey anmelden"; Yubikeys
funktionieren im FIDO2-Modus direkt (kein TOTP-Umweg nötig). Verwaltung der
Passkeys im Header unter Sicherheit. Hinweis: Passkeys sind an die Domain
gebunden — wer die Domain wechselt, muss sie neu registrieren.
SSO / OIDC einrichten
VSM_OIDC_ENABLED=true
VSM_OIDC_DISCOVERY=https://idp.example.com/.well-known/openid-configuration
VSM_OIDC_CLIENT_ID=vserver-manager
VSM_OIDC_CLIENT_SECRET=…
VSM_OIDC_REDIRECT=https://vsm.example.com/api/auth/oidc/callback
Beim IdP die Redirect-URI exakt eintragen. Ist OIDC aus, blendet die SPA den SSO-Button aus und es gilt nur die lokale Anmeldung.
Das ID-Token wird vollständig geprüft: Signatur gegen das JWKS des Providers
(per kid, mit automatischem Neuladen bei Key-Rotation), iss gegen den
Discovery-Issuer, aud gegen die Client-ID, dazu exp/iat und ein nonce
gegen Replay. Erlaubte Signaturalgorithmen kommen aus VSM_OIDC_ALGS (Default
RS/ES/PS-Familie) — niemals aus dem Token-Header, was alg=none und
alg-confusion ausschließt.
Gruppen-Mapping (superadmin)
Die globale Rolle lässt sich aus einer IdP-Gruppe ableiten:
VSM_OIDC_SCOPES=openid email profile groups
VSM_OIDC_GROUPS_CLAIM=groups
VSM_OIDC_SUPERADMIN_GROUPS=vsm-admins
VSM_OIDC_GROUP_SYNC=true
Wer in vsm-admins ist, bekommt bei der Anmeldung superadmin, alle anderen
VSM_OIDC_DEFAULT_ROLE (Default user). Groß-/Kleinschreibung und ein
führendes / (Keycloak liefert /vsm-admins) spielen keine Rolle; ein
CN=…-Präfix aus LDAP-Brücken wird ebenfalls abgeschnitten. Da die Liste an
Kommas getrennt wird, gehören dort einfache Gruppennamen hinein, keine
vollständigen DNs.
Verschachtelte Claims erreicht man per Punktpfad, z. B. für Keycloak-Client-Rollen:
VSM_OIDC_GROUPS_CLAIM=resource_access.vserver-manager.roles
Bei VSM_OIDC_GROUP_SYNC=true (Default) wird bei jeder Anmeldung neu
ausgewertet: Wer die Gruppe im IdP verliert, fällt hier zurück auf
VSM_OIDC_DEFAULT_ROLE, und seine token_version wird erhöht — laufende
Sitzungen mit der alten Rolle gelten damit sofort als ungültig. Jede Änderung
landet als oidc_role_sync im Audit-Log.
Zwei Sicherungen sind eingebaut:
- Ist
VSM_OIDC_SUPERADMIN_GROUPSleer, findet kein Mapping statt — ein versehentlich leerer Wert stuft also niemanden herunter. - Der letzte aktive
superadminwird nie automatisch herabgestuft. Sonst könnte ein Tippfehler im Gruppennamen die Instanz führungslos machen. Der übersprungene Entzug wird als fehlgeschlagener Audit-Eintrag protokolliert.
Liefert der IdP die Gruppen nicht im ID-Token, wird zusätzlich der
userinfo-Endpunkt abgefragt (VSM_OIDC_USERINFO_FALLBACK=true).
Hinweise zum Betrieb
- Erste Tabs erscheinen erst nach Auswahl eines Servers links in der Liste.
.nix-Validierung brauchtnixauf dem Zielserver (sonst greift der reine Klammer-Balance-Check als Fallback).- Docker-Aktionen setzen voraus, dass der Wartungsbenutzer Docker per sudo bedienen darf.
- Die Datenbank (SQLite-Default) liegt unter
VSM_DATA_DIR(Default./data). SQLite läuft im WAL-Modus, es liegen also zusätzlichvsm.db-walundvsm.db-shmdaneben — die gehören zum Datenbestand und müssen in ein Backup. - Für PostgreSQL zusätzlich den Treiber installieren:
pip install "psycopg[binary]", dannVSM_DATABASE_URLsetzen. Schema- Migrationen laufen dort genauso automatisch wie unter SQLite und werden in der CI gegen eine echte PostgreSQL-Instanz geprüft.
Lizenz / Gewähr
Bereitgestellt „as is" als Grundlage zum Weiterbauen. Vor Produktivnutzung die oben genannten Punkte (TLS, Secret-Management, ggf. geteilter Store für Multi-Worker) berücksichtigen.