From 40bc8913e71b0acd97e9547c218bb3e9b642b588 Mon Sep 17 00:00:00 2001 From: arnol Date: Fri, 7 Aug 2026 19:05:45 +0200 Subject: [PATCH] Showcase nachgezogen: restliche Portfolio-Dateien auf aktuellen Stand --- 04-Management-Desktop-CT102.md | 137 +++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) diff --git a/04-Management-Desktop-CT102.md b/04-Management-Desktop-CT102.md index 73723fe..61ad30b 100644 --- a/04-Management-Desktop-CT102.md +++ b/04-Management-Desktop-CT102.md @@ -154,6 +154,143 @@ sftp-root-directory = / ``` Passwort nicht als Parameter setzen — wird beim ersten Datei-Transfer-Zugriff im Guacamole-Seitenpanel abgefragt. +## 8. Auto-Stop bei Nichtnutzung + Wake-Mechanismus (Stromsparen) + +CT102 läuft dauerhaft (~335 MB RAM + etwas CPU im Leerlauf), auch wenn niemand per VNC verbunden ist. Ziel: automatisch stoppen, wenn ungenutzt, mit einfachem Weg zum Wiederstarten. + +⚠️ **NICHT TUN:** `pct suspend`/`resume` (CRIU-Checkpointing) für einen echten Ruhezustand verwenden. Proxmox unterstützt das für Container nur experimentell — für einen vollen Desktop-Prozessbaum (X11/VNC/Firefox) real riskant (bekannte CRIU-Einschränkungen bei komplexen Prozessbäumen/offenen Sockets). Stattdessen: komplett stoppen und bei Bedarf neu starten — bei einem LXC dauert das nur Sekunden, kein Kernel-Boot wie bei einer echten VM. + +### Wake-Mechanismus (spiegelt "Wake PC1" aus `01-pve-host.md` Abschnitt 6) + +```bash +useradd -m -s /bin/bash browserwake +ssh-keygen -t ed25519 -N "" -f /home/browserwake/.ssh/browserwake_key + +# WICHTIG: Optionen und Schlüssel MÜSSEN auf einer einzigen Zeile stehen! +PUBKEY=$(cat /home/browserwake/.ssh/browserwake_key.pub) +echo "no-port-forwarding,no-x11-forwarding,no-agent-forwarding,command=\"/usr/local/bin/start-browser-desktop.sh\" ${PUBKEY}" \ + > /home/browserwake/.ssh/authorized_keys + +apt-get install -y sudo +cat > /etc/sudoers.d/browserwake <<'EOF' +browserwake ALL=(root) NOPASSWD: /usr/sbin/pct start 102 +EOF +chmod 440 /etc/sudoers.d/browserwake +``` + +`/usr/local/bin/start-browser-desktop.sh`: +```bash +#!/usr/bin/env bash +sudo /usr/sbin/pct start 102 +echo "Management-Desktop (CT102) gestartet (oder lief bereits)." +``` + +Neue Guacamole-SSH-Verbindung "Management-Desktop starten" (`hostname=`, `port=22`, `username=browserwake`, `private-key`, `command=/usr/local/bin/start-browser-desktop.sh`) — gleicher Aufbau wie "Wake PC1". Nutzung: erst diese Verbindung anklicken (paar Sekunden warten), danach die normale "Management-Desktop (Browser)"-VNC-Verbindung öffnen. + +⚠️ **NICHT TUN:** Optionen (`no-port-forwarding,...,command="..."`) und den Public-Key-String auf zwei separate Zeilen in `authorized_keys` schreiben (z.B. weil man die Optionen erst per `echo` schreibt und den Key danach per `>>` anhängt). SSHs Format verlangt **beides auf einer Zeile** — zwei Zeilen ergeben eine kaputte erste Zeile (wird ignoriert) und einen **komplett unrestriktiven** zweiten Schlüssel ohne jede Einschränkung. Beim ersten Testlauf genau so passiert und erst durch einen expliziten Test (`ssh -i key user@host "beliebiger befehl"` — führte den beliebigen Befehl aus statt des erzwungenen Skripts) aufgefallen. Nach jeder `authorized_keys`-Änderung mit forced command **immer** gegentesten: ein beliebiger Befehl darf niemals durchkommen. + +`pct` erfordert Root-Rechte — ein unprivilegierter Linux-User kann es nicht direkt aufrufen (`pct: command not found` bzw. Permission-Fehler). Deshalb die eng begrenzte `sudo`-Regel nur für exakt `pct start 102`, nicht für `pct` allgemein. + +### Auto-Stop bei Inaktivität + +`/usr/local/bin/browser-desktop-idle-check.sh`, Cron alle 5 Minuten. Finale Version prüft die tatsächliche **X11-Eingabe-Idle-Zeit** (`xprintidle`, direkt im Container gemessen) statt eines Verbindungsstatus: + +```bash +IDLE_THRESHOLD_MS=900000 # 15 Minuten +STATUS=$(/usr/sbin/pct status 102 2>/dev/null | awk '{print $2}') +[ "$STATUS" != "running" ] && exit 0 + +IDLE_MS=$(/usr/sbin/pct exec 102 -- env DISPLAY=:1 XAUTHORITY=/root/.Xauthority xprintidle 2>/dev/null) +case "$IDLE_MS" in ''|*[!0-9]*) exit 0 ;; esac + +if [ "$IDLE_MS" -ge "$IDLE_THRESHOLD_MS" ]; then + /usr/sbin/pct shutdown 102 --timeout 30 + # + ntfy-Push +fi +``` + +**Vorgeschichte — drei verworfene Verbindungsstatus-Ansätze, bevor die eigentliche Ursache klar wurde:** + +⚠️ **NICHT TUN (erste Version):** Aktive Verbindung per `pct exec 102 -- ss ...` **innerhalb** des Containers prüfen — die eingehende Verbindung ist dort nicht sichtbar, wohl aber vom PVE-Host aus (unter der Tailscale-IP des Containers). + +⚠️ **NICHT TUN:** Auch die host-seitige `ss`-Prüfung auf Port 5901 ist nicht zuverlässig. Reale Beobachtung: eine Guacamole-Sitzung endete laut eigener `guacamole_connection_history`-Tabelle sauber nach 15 Sekunden — `ss` zeigte aber über Stunden weiterhin ESTABLISHED. Vermutlich hält `guacd` die Backend-Verbindung länger offen als die Nutzer-Sitzung dauert. + +⚠️ **NICHT TUN:** Guacamoles eigene History-Tabelle (`end_date IS NULL` = aktive Sitzung) abfragen — es gab verwaiste Einträge ohne `end_date` von nie sauber beendeten alten Sitzungen, nicht von einer echten laufenden Sitzung unterscheidbar. + +Direkter Check im `guacd`-Container selbst (`docker exec guacd netstat -tn`) war technisch die zuverlässigste der drei TCP-Varianten — **hatte aber einen grundsätzlichen, nicht behebbaren blinden Fleck:** eine offene Verbindung sagt nichts darüber aus, ob der Nutzer gerade AFK ist. Bleibt der Guacamole-Tab offen und der Nutzer geht weg, bleibt die TCP-Verbindung bestehen — der Auto-Stop hätte nie ausgelöst. Erst ein Live-Test (Nutzer bewusst AFK mit offenem Tab) deckte das auf. + +⚠️ **NICHT TUN (zusätzlicher Bug, unabhängig vom obigen):** `pct` ohne absoluten Pfad aufrufen. Cron läuft mit `PATH=/usr/bin:/bin` (ohne `/usr/sbin`, wo `pct` liegt) — Aufrufe ohne absoluten Pfad schlagen unter Cron mit "not found" fehl, `STATUS` bleibt leer, das Skript nimmt den "läuft nicht"-Zweig und erreicht die eigentliche Prüfung nie. Gefunden durch gezieltes Nachstellen der Cron-Umgebung (`env -i ... PATH=/usr/bin:/bin`). Lektion: bei "funktioniert manuell, aber nicht unter Cron" immer zuerst die PATH-Differenz prüfen. + +**Finale Lösung:** `xprintidle` misst die Zeit seit der letzten echten Maus-/Tastatur-Eingabe auf X11-Ebene, unabhängig vom Verbindungsstatus — löst das AFK-Problem strukturell und macht das bisherige Zähler-Statefile überflüssig (Idle-Zeit ist bereits selbst der Zustand). + +**Verifikation:** Wake-Key-Test (erzwungener Befehl + Port-Forwarding) bestanden. Auto-Stop unter simulierter Cron-Umgebung (minimaler PATH) mit künstlich niedrigem Threshold end-to-end getestet: Container wurde korrekt gestoppt. + +### Screen-Lock nach 3 Min. Inaktivität (Enterprise-Richtlinie als Orientierung) + +Zusätzlich zum Auto-Stop: die Desktop-Sitzung selbst soll sich sperren, wenn niemand aktiv ist — unabhängig vom Auto-Stop-Timer. `xautolock` ist nicht paketiert (Debian ), daher ein eigener minimaler Watcher statt eines fertigen Tools. + +⚠️ **NICHT TUN (erste Version):** `xtrlock` als Locker einsetzen. Funktional korrekt, aber `xtrlock` ist ein reines Xlib-Programm ohne jede Unterstützung für die X-Zwischenablage — entsperren geht nur durch **blindes Eintippen** des Passworts, ein Paste aus einem Passwortmanager (`xdotool key ctrl+v` gegen den laufenden Lock getestet) hat nachweislich **keine** Wirkung. Für ein starkes, zufälliges Passwort aus dem Passwortmanager unpraktikabel. `i3lock`/`slock` sind dieselbe Xlib-Keygrab-Familie (Debian-Tag `uitoolkit::xlib`) und hätten dieselbe Einschränkung. + +⚠️ **NICHT TUN (zweiter Versuch):** `xfce4-screensaver` (GTK-basiert, `uitoolkit::gtk`, unterstützt Paste in der Theorie) als Ersatz einsetzen. In dieser konkreten Umgebung (TigerVNC-Session ohne Display-Manager-Login, ohne `xfce4-power-manager`, `Xtigervnc` ohne DPMS-Extension) funktioniert weder die manuelle Sperr-Anweisung (`xfce4-screensaver-command --lock` hängt den Daemon auf, D-Bus-Anfragen laufen danach in einen Timeout) noch die automatische Idle-Auslösung (kein `xfce4-power-manager`, das das nötige Idle-Signal liefert). Der Daemon selbst crasht nicht, wird aber dauerhaft unresponsive — ein reales Risiko, den Desktop komplett gesperrt und unentsperrbar zu hinterlassen. Deshalb wieder deinstalliert. + +**Finale Lösung:** ein minimaler, selbst geschriebener Python/Tkinter-Lock-Dialog (`python3-tk`) — echtes GUI-Textfeld, Paste funktioniert standardmäßig (`tkinter.Entry` unterstützt Strg+V ohne Zusatzaufwand), kein Display-Manager/D-Bus-Session-Dienst nötig. + +`/usr/local/bin/gui-lock.py`: +```python +import tkinter as tk +SECRET_FILE = "/root/.config/lock-secret" + +def check_and_maybe_unlock(event=None): + with open(SECRET_FILE) as f: + secret = f.read().strip() + if entry.get() == secret: + root.destroy() + else: + entry.delete(0, tk.END) + root.bell() + +root = tk.Tk() +root.attributes("-fullscreen", True) +root.attributes("-topmost", True) +root.configure(bg="black") +root.config(cursor="none") +label = tk.Label(root, text="Gesperrt — Passwort eingeben", fg="white", bg="black", font=("sans", 18)) +label.pack(pady=(300, 20)) +entry = tk.Entry(root, show="*", font=("sans", 18), width=30, justify="center") +entry.pack() +entry.bind("", check_and_maybe_unlock) +entry.focus_force() +root.wait_visibility() # sonst: "grab failed: window not viewable" +root.grab_set_global() +root.mainloop() +``` + +Vergleichs-Passwort steht in `/root/.config/lock-secret` (chmod 600) — **kein** PAM/Linux-Account-Passwort, eigenständiges Secret nur für diesen Lock. Dadurch komplett unabhängig vom Root-Account, kein SSH-Bezug, keine PAM-Komplexität. + +`/usr/local/bin/lock-on-idle.sh`: +```bash +#!/bin/sh +THRESHOLD_MS=180000 # 3 Minuten +while true; do + IDLE=$(xprintidle 2>/dev/null || echo 0) + [ "$IDLE" -ge "$THRESHOLD_MS" ] && python3 /usr/local/bin/gui-lock.py + sleep 10 +done +``` + +`/root/.config/tigervnc/xstartup`: +```sh +#!/bin/sh +unset SESSION_MANAGER +unset DBUS_SESSION_BUS_ADDRESS +/usr/local/bin/lock-on-idle.sh & +exec startxfce4 +``` + +⚠️ **NICHT TUN:** `root.grab_set_global()` direkt nach dem Erstellen des Fensters aufrufen — Tk wirft `TclError: grab failed: window not viewable`, weil das Fenster vom Window-Manager noch nicht gemappt wurde. Vorher `root.wait_visibility()` aufrufen. + +**Verifikation:** Watcher-Loop mit künstlich niedrigem Threshold end-to-end getestet — löst automatisch aus, ohne manuellen Aufruf. Entsperren per `xdotool`-simuliertem Strg+V-Paste (aus der X-Zwischenablage) erfolgreich getestet. Gegentest mit falschem Passwort bestätigt: Lock bleibt bestehen. Getipptes (nicht eingefügtes) Passwort funktioniert weiterhin normal. + ## Verifikation ```bash