No description
  • Python 73.8%
  • Vue 20.6%
  • Shell 2.3%
  • JavaScript 1.9%
  • CSS 0.8%
  • Other 0.5%
Find a file
Philipp Skotnik 9d031cfbc5
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
moved global SSH Key MGMT to Security Tag
2026-08-09 23:31:24 +02:00
.github/workflows Redo previous CI Fixes cause PostgreSQL 2026-08-08 19:10:35 +02:00
backend UI Tweaks Part 2 and initial API Token support 2026-08-09 23:20:44 +02:00
deploy Various Optimisations to reduce needed resources 2026-08-07 09:37:42 +02:00
frontend-vue moved global SSH Key MGMT to Security Tag 2026-08-09 23:31:24 +02:00
migrations UI Tweaks Part 2 and initial API Token support 2026-08-09 23:20:44 +02:00
mobile Completed VUE Migration, added OIDC Group Mapping 2026-08-06 19:50:31 +02:00
scripts Fixed CI ... again fuck PostgreSQL 2026-08-07 22:22:56 +02:00
tests UI Tweaks Part 2 and initial API Token support 2026-08-09 23:20:44 +02:00
.env.example UI Tweaks Part 2 and initial API Token support 2026-08-09 23:20:44 +02:00
.gitignore further Performance Tweaks and Security Enhancements 2026-08-07 12:22:11 +02:00
alembic.ini further Performance Tweaks and Security Enhancements 2026-08-07 12:22:11 +02:00
CONTRIBUTING.md UI Tweaks Part 2 and initial API Token support 2026-08-09 23:20:44 +02:00
manage.py Refined Audit logging, Implemented Fleet Support v2, and Backup / Restore as well as sudoers editing 2026-08-08 19:03:15 +02:00
pyproject.toml Fixed CI 2026-08-07 21:57:45 +02:00
README.md UI Tweaks Part 2 and initial API Token support 2026-08-09 23:20:44 +02:00
requirements-dev.txt further Performance Tweaks and Security Enhancements 2026-08-07 12:22:11 +02:00
requirements.txt further Performance Tweaks and Security Enhancements 2026-08-07 12:22:11 +02:00
run.sh further Performance Tweaks and Security Enhancements 2026-08-07 12:22:11 +02:00

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 / adminsofort ä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.local bearbeiten (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 moderner signed-by/keyrings-Methode; RHEL/Fedora COPR (dnf copr), Repo per .repo-URL (config-manager) und rpm --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_keys eines 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 und up -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 (offizielles get.docker.com-Skript inkl. Compose-Plugin, Dienst wird aktiviert). Von VSM angelegte Compose-Projekte bleiben nach down in 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 über nix-instantiate auf 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 über visudo -c (erst prüfen, dann verschieben). Deutlich enger als NOPASSWD: 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, damit web, Web und web nicht 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 postgres auf 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_PERIOD und VSM_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_runs erhalten.
  • 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_SECRET abgeleitet) 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 N führt sonst jeder Worker die geplanten Aufgaben aus — genau einen Prozess mit VSM_SCHEDULER=true betreiben, alle übrigen mit false.
  • 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 dessen auth.log. Abschaltbar über VSM_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:

  1. VSM_SECRET wird 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.
  2. Admin-Passwort: ist VSM_ADMIN_PASSWORD nicht gesetzt, wird beim ersten Start ein zufälliges erzeugt und einmalig ins Log geschrieben. Danach in der App unter „Sicherheit“ änderbar.
  3. 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.
  4. Least Privilege: je Server einen dedizierten Wartungsbenutzer mit nur den nötigen sudo-Rechten statt root.
  5. 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_GROUPS leer, findet kein Mapping statt — ein versehentlich leerer Wert stuft also niemanden herunter.
  • Der letzte aktive superadmin wird 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 braucht nix auf 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ätzlich vsm.db-wal und vsm.db-shm daneben — die gehören zum Datenbestand und müssen in ein Backup.
  • Für PostgreSQL zusätzlich den Treiber installieren: pip install "psycopg[binary]", dann VSM_DATABASE_URL setzen. 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.