diff --git a/README.md b/README.md index 3b8fc5e..9a848c4 100644 --- a/README.md +++ b/README.md @@ -369,9 +369,20 @@ pvesnap-explorer --cleanup pvesnap-explorer # Gast und Snapshot auswählen pvesnap-explorer 802 # Snapshot von VM 802 auswählen pvesnap-explorer 802 # direkt öffnen +pvesnap-explorer --local /srv/restore # rechtes Fenster startet dort pvesnap-explorer --path /mnt/x # beliebiges Verzeichnis (zum Ausprobieren) +pvesnap-explorer --cleanup # hängengebliebene Einbindungen lösen ``` +| Parameter | Vorgabe | Bedeutung | +|---|---|---| +| `vmid` | — | Gast direkt vorwählen | +| `snapshot` | — | Snapshot direkt vorwählen | +| `-l`, `--local ` | aktuelles Verzeichnis | Startverzeichnis im rechten Fenster | +| `-p`, `--path ` | — | beliebiges Verzeichnis öffnen, ohne Proxmox zu fragen | +| `--cleanup` | — | hängengebliebene Einbindungen lösen und beenden | +| `-v`, `--verbose` | — | ausführliches Protokoll | + Links der Snapshot, rechts der lokale Rechner: | Taste | | @@ -401,28 +412,94 @@ bleiben Verweise. ### `pvesnap-web` — im Browser herunterladen -Als Dienst (empfohlen, überlebt Neustarts): +#### Als Dienst einrichten ```bash ./install.sh --with-webexplorer --port 8823 ``` -Danach läuft die Oberfläche dauerhaft auf `http://:8823/`. Port, Adresse -und Anmeldeart stehen in `/etc/pvesnap/web.conf`: +Danach läuft die Oberfläche dauerhaft auf `http://:8823/` und startet mit +dem System. Ohne `--port` bricht das Skript ab — der Port hat keine sinnvolle +Vorgabe, die zu jedem Host passt. + +```bash +./install.sh --with-webexplorer --port 8443 --bind 10.0.0.5 # nur auf einer IP +./install.sh --with-webexplorer --port 8823 --no-start # erst später starten +``` + +Alle Aufrufparameter des Dienstes stehen in **`/etc/pvesnap/web.conf`**: ``` PVESNAP_WEB_ARGS=--bind 0.0.0.0 --port 8823 --auth pve ``` -Nach Änderungen `systemctl restart pvesnap-web`. Oder von Hand für einen -einzelnen Einsatz: +Dort lässt sich alles nachträglich ändern — Port, Adresse, Anmeldeart, Rechte: ```bash -pvesnap-web # Gast und Snapshot im Browser wählen -pvesnap-web 802 # diesen Snapshot gleich öffnen -pvesnap-web --path /verzeichnis # beliebiges Verzeichnis (ohne Proxmox) +nano /etc/pvesnap/web.conf +systemctl restart pvesnap-web ``` +Dienst verwalten: + +```bash +systemctl status pvesnap-web # läuft er? +systemctl restart pvesnap-web # nach Änderungen in web.conf +systemctl stop pvesnap-web # anhalten (hängt offene Snapshots aus) +systemctl disable pvesnap-web # nicht mehr automatisch starten +journalctl -u pvesnap-web -f # Protokoll, auch fehlgeschlagene Anmeldungen +``` + +#### Von Hand starten + +Für einen einmaligen Einsatz, ohne etwas zu installieren: + +```bash +pvesnap-web # Gast und Snapshot im Browser wählen +pvesnap-web 802 # nur diesen Gast anbieten +pvesnap-web 802 autosnap-taeglich-20260731-023000 # Snapshot gleich öffnen +``` + +| Parameter | Vorgabe | Bedeutung | +|---|---|---| +| `-P`, `--port ` | `8823` | Port, auf dem gelauscht wird | +| `-b`, `--bind ` | `0.0.0.0` | Netzwerkadresse; `127.0.0.1` = nur lokal | +| `-a`, `--auth pve\|token\|none` | `pve` | Anmeldeart, siehe unten | +| `--require-privilege ` | `VM.Snapshot` | nötiges Recht auf dem Gast | +| `--allow-user ` | — | dieser Benutzer darf alles (mehrfach möglich) | +| `-t`, `--token ` | zufällig | Zugangsschlüssel selbst vorgeben | +| `--no-token` | — | Kurzform für `--auth none` | +| `-p`, `--path ` | — | beliebiges Verzeichnis statt eines Snapshots | +| `--cleanup` | — | hängengebliebene Einbindungen lösen und beenden | +| `-v`, `--verbose` | — | ausführliches Protokoll | +| `-V`, `--version` | — | Version anzeigen | + +Beispiele: + +```bash +# Nur lokal lauschen, dazu vom Arbeitsplatz einen SSH-Tunnel legen +pvesnap-web --bind 127.0.0.1 --port 8823 +# am eigenen Rechner: ssh -L 8823:localhost:8823 root@pve +# dann im Browser: http://localhost:8823/ + +# Ein Backup-Team soll ran, ohne Snapshot-Rechte zu haben +pvesnap-web --port 8823 --require-privilege VM.Backup + +# Ein einzelner Dienstleister, unabhängig von den Proxmox-Rechten +pvesnap-web --port 8823 --allow-user dienstleister@pve + +# Schnell etwas herausgeben, ohne Proxmox-Benutzer (Schlüssel in der Adresse) +pvesnap-web --port 8823 --auth token + +# Ein beliebiges Verzeichnis freigeben, z. B. ein bereits gemountetes Backup +pvesnap-web --path /mnt/restore --port 8823 + +# Nach einem Absturz aufräumen +pvesnap-web --cleanup +``` + +Mit `pvesnap-web --help` gibt es dieselbe Übersicht im Terminal. + **Anmeldung mit den Proxmox-Benutzern.** Die Oberfläche zeigt dieselbe Anmeldemaske wie Proxmox — Benutzername, Passwort, Realm-Auswahl. Geprüft wird über `POST /access/ticket` auf der lokalen Proxmox-API, also genau den Weg, den diff --git a/pvesnap/web/server.py b/pvesnap/web/server.py index 3a75034..ceeebf3 100644 --- a/pvesnap/web/server.py +++ b/pvesnap/web/server.py @@ -85,13 +85,16 @@ class AppState: """Was gerade geoeffnet ist. Es ist immer hoechstens ein Snapshot offen.""" def __init__(self, token="", proxmox=None, fixed_path="", auth_mode="token", - privilege=DEFAULT_PRIVILEGE, allowed_users=()): + privilege=DEFAULT_PRIVILEGE, allowed_users=(), only_vmid=None): self.token = token self.proxmox = proxmox self.fixed_path = fixed_path self.auth_mode = auth_mode # pve | token | none self.privilege = privilege self.allowed_users = tuple(allowed_users or ()) + # Wurde beim Start eine VMID angegeben, gibt es auch nur diesen Gast - + # weder in der Liste noch ueber die Adresszeile. + self.only_vmid = only_vmid self.sessions = Sessions() self.session = None self.guest = None @@ -107,6 +110,8 @@ class AppState: def visible_guests(self, userid): """Nur die Gaeste, in die dieser Benutzer hineinsehen darf.""" guests = self.proxmox.inventory(refresh=True) + if self.only_vmid: + guests = [g for g in guests if g.vmid == self.only_vmid] if self.auth_mode != "pve": return guests tree = permissions(userid) @@ -114,6 +119,8 @@ class AppState: if may_access(userid, g, self.privilege, tree, self.allowed_users)] def may_open(self, userid, guest): + if self.only_vmid and guest.vmid != self.only_vmid: + return False if self.auth_mode != "pve": return True return may_access(userid, guest, self.privilege, None, self.allowed_users) @@ -905,7 +912,7 @@ def main(argv=None): token = (args.token or secrets.token_urlsafe(18)) if mode == "token" else "" state = AppState(token=token, proxmox=proxmox, fixed_path=args.path or "", auth_mode=mode, privilege=args.require_privilege, - allowed_users=args.allow_user) + allowed_users=args.allow_user, only_vmid=args.vmid) if args.vmid and args.snapshot and proxmox: guest = next((g for g in proxmox.inventory() if g.vmid == args.vmid), None)