Files
tesm/README.md
T
alientimandClaude Sonnet 5 06db4c1382 Switche: optionaler SSH-Port statt fest 22
- Neue Spalte switches.ssh_port (nullable INTEGER), Migration für
  bestehende DBs in _ensure_schema(), Feld in create_db.py ergänzt.
- Formular (Anlegen/Bearbeiten): optionales SSH-Port-Feld, Validierung
  1-65535, leer -> NULL (= Standard 22 überall, SWITCH_DEFAULT_SSH_PORT).
  Switch-Liste zeigt den effektiven Port inkl. "(Standard)"-Hinweis.
- generate_ips.py liefert den effektiven Port (Fallback 22) als eigenes
  Pipe-Feld an poe.sh; poe.sh übernimmt es in disable_poe/enable_poe und
  reicht es als "ssh -p <port>" an die expect-Skripte durch (Default
  weiterhin 22 falls Parameter fehlt).
- Web-Terminal (Verbindungstest) sendet den im Formular eingetragenen
  Port statt hartkodiert 22.
- Live getestet: gültiger/leerer/ungültiger Port beim Anlegen, korrekte
  Weitergabe durch generate_ips.py inkl. Feldreihenfolge, die poe.sh
'read' erwartet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 19:56:23 +02:00

402 lines
22 KiB
Markdown

# PoE Manager — Modern UI Rebuild
Webbasierte Verwaltung und Monitoring von PoE-Devices und Aruba-Switchen —
funktionales Rebuild von [Aruba-PoE](https://gitea.int.eertmoed.net/WiS/Aruba-PoE)
mit komplett neuem, modernem Frontend und einem granularen Rechtesystem.
**Das Projekt ist ausschließlich für Linux ausgelegt** (Zielsystem: eine
Linux-VM). Entwickelt wird unter WSL2 (Ubuntu) unter Windows — siehe
[Entwicklung unter WSL](#entwicklung-unter-wsl) weiter unten.
Die App ermöglicht:
- **Öffentliches Kachel-Dashboard ohne Login** — zeigt nur aktive (nicht
deaktivierte) Geräte, nur Online/Offline/Gesamt-Statistik; volle Verwaltung,
Aktivieren/Deaktivieren und PoE-Neustart bleiben hinter dem Login. Eingeloggt
sind zusätzlich deaktivierte Geräte sichtbar (eigene Kachel-Sektion). Alle
Kacheln sind durchsuchbar und in drei Abschnitten sortiert: Offline, Online,
Deaktiviert — jeweils alphabetisch
- Verwaltung von Devices und Switches
- **Zugangsdaten als eigene, wiederverwendbare Entität**: SSH-Logins werden
einmal angelegt und können mehreren Switchen zugeordnet werden; beim
Anlegen eines Switches lassen sich bestehende Zugangsdaten auswählen oder
direkt neue hinterlegen, ohne vorher zu „Zugangsdaten“ wechseln zu müssen
- **Browser-SSH-Terminal beim Anlegen/Bearbeiten eines Switches**, um den
Host-Key zu bestätigen und die Zugangsdaten direkt zu prüfen, bevor der
Switch gespeichert wird — funktioniert auf jedem Betriebssystem, die
SSH-Verbindung wird dabei immer vom PoE-Server aus aufgebaut
- Manueller PoE-Neustart einzelner Geräte per Klick; bei deaktivierten
Geräten bietet das Detail-Popup stattdessen direkt eine Aktivieren-Option
- Einstellbares Prüfintervall (steuert den Hintergrund-Check-Dienst) —
der Countdown „Nächste Prüfung“ wird aus dem tatsächlichen letzten
Log-Eintrag berechnet, nicht bei jedem Seitenaufruf neu gestartet, und
läuft **global in der Topbar auf jeder Seite** (nicht nur im Dashboard)
- Live-Log-Ansicht mit farblicher Hervorhebung (online/offline/Neustart);
Dashboard und Live-Log aktualisieren sich automatisch im Hintergrund
(per AJAX, ohne vollen Seiten-Reload). Ein „Jetzt prüfen"-Icon neben dem
Timer (nur Admins) startet manuell einen sofortigen Prüf-Durchlauf —
der Countdown setzt sich dabei auf den manuell ausgelösten Zeitpunkt zurück
- **Einheitliches Seiten-Layout**: die Topbar zeigt rechts ausschließlich
den „Nächste Prüfung“-Timer; Anlegen-Buttons und eine kurze Beschreibung
sitzen stattdessen direkt über der jeweiligen Tabelle. Die Listen
(Geräte, Switche, Zugangsdaten, Benutzer, Gruppen) sind per Klick auf die
Spaltenüberschrift sortierbar
- **Rechtesystem mit Gruppen**: Admins dürfen immer alles (als Gruppe „Admin“
überall mit aufgeführt); normalen Benutzern lassen sich über Gruppen
einzelne Verwaltungsrechte gezielt zuweisen, inkl. reiner Ansichtsrechte
(siehe [Rechtesystem](#rechtesystem-gruppen--berechtigungen))
- Einklappbare Sidebar (Desktop: dauerhaft, über Neuladen hinweg gemerkt;
mobil: temporäres Überlagern)
- **Änderungslog**: jede Anlage/Bearbeitung/Löschung sowie Aktivieren/
Deaktivieren von Geräten, Switchen, Zugangsdaten, Benutzern und Gruppen
wird protokolliert (wer, wann, was) — einsehbar unter „Logs → Änderungslog“;
Geräte und Switche merken sich zusätzlich direkt am Datensatz, wer sie
zuletzt geändert hat. PoE-Neustarts werden bewusst **nicht** geloggt
(siehe [Änderungslog](#änderungslog))
- **Verschlüsselter Import/Export**: Geräte, Switche und Zugangsdaten lassen
sich unter „Einstellungen → Im-/Export" als ein mit selbstgewählter
Passphrase verschlüsseltes Bundle exportieren und auf einer neuen Umgebung
wieder importieren (siehe [Import/Export](#importexport))
- **Eigenes Konto**: das Zahnrad-Symbol neben dem eigenen Namen unten in der
Sidebar führt zu „Mein Konto“ — Vor-/Nachname, eigenes Passwort und
Profilbild pflegen (siehe [Eigenes Konto](#eigenes-konto--profilbild))
- **Anpassbare Navigation**: Admins legen dort zusätzlich die Reihenfolge der
Sidebar-Hauptpunkte per Auf-/Ab-Buttons fest; jeder Benutzer sieht davon
ohnehin nur, wofür er berechtigt ist — auch pro Unterpunkt innerhalb einer
Gruppe (siehe [Navigation anpassen](#navigation-anpassen))
## Was ist neu gegenüber dem Original?
- **Komplett neues Frontend**: eigenes Design-System (kein Bootstrap mehr),
Logo mittig in der Topbar (auf jeder Seite, skaliert auf die Balkenhöhe),
Sidebar-Navigation (nur für eingeloggte Nutzer sichtbar), Dark/Light-
Umschalter, Karten-Dashboard mit Live-Zählern, moderne Modals/Toasts statt
Browser-`confirm()` und Bootstrap-Alerts.
- **Dashboard jetzt teilweise öffentlich**: `/` ist ohne Login erreichbar und
zeigt eine reduzierte Lese-Ansicht (Hostname, IP, Status, letzte Prüfung)
mit Login-Button; alle Verwaltungsseiten und der PoE-Neustart bleiben
`@login_required` und zusätzlich rechtebasiert (s.u.).
- **Browser-SSH-Terminal für Switche**, implementiert mit **paramiko** (reines
Python) statt eines lokalen pty + System-`ssh`-Client — läuft dadurch
plattformunabhängig. Die SSH-Verbindung zum Switch wird immer serverseitig
(vom PoE-Server) aufgebaut, nie vom Browser/Client aus.
- **Granulares Rechtesystem mit Gruppen** (neu, siehe unten) statt der
ursprünglichen reinen Admin/User-Unterscheidung.
- **Gleiche Kernlogik**: Datenbankschema (erweitert um Gruppen/Rechte),
Login, PoE-Restart-Mechanismus (`poe.sh` per SSH/`expect` auf die
Aruba-Switche) sind ansonsten unverändert.
- **Aufgeräumtes Backend**: konsistente, konfigurierbare Pfade (`POE_DB_PATH`,
`POE_SCRIPT`, `POE_LOG_GLOB`, …) statt hart codierter/inkonsistenter Pfade;
Secret-Key und Fernet-Key werden beim ersten Start automatisch generiert
statt im Repo zu liegen.
> ⚠️ **Sicherheitshinweis zum Original-Repo:** Im Original sind `sqlite.db`
> (Nutzer-Passwort-Hashes, verschlüsselte Switch-Passwörter) und `fernet.key`
> (der zugehörige Schlüssel) mit im Git-Repo eingecheckt — die `.gitignore`
> dort schließt sie über die `!`-Regeln versehentlich wieder ein. In diesem
> Rebuild sind beide Dateien in `.gitignore` ausgeschlossen und werden beim
> ersten Start frisch erzeugt. Empfehlung fürs Original-Repo: beide Dateien
> aus der Git-Historie entfernen und Fernet-Key sowie alle Switch-/User-
> Passwörter rotieren.
## Rechtesystem (Gruppen & Berechtigungen)
Admins dürfen immer alles. Zusätzlich gibt es **Gruppen** mit einem
**vollständig granularen, an der Navbar gespiegelten Rechtebaum** — jeder
Bereich der App (nicht nur Devices/Switches) lässt sich einzeln freischalten.
Ein Benutzer kann mehreren Gruppen angehören, die Rechte addieren sich
(Vereinigung, nicht Schnittmenge).
### Zweistufiger Rechtebaum
Der Baum hat genau zwei Ebenen, exakt gespiegelt an den drei Sidebar-Gruppen.
Pro Bereich gibt es eine kompakte Tabelle: Zeilen = der Bereich selbst
„(gesamt)“ + je ein Unterpunkt, Spalten = die Rechte **R**(ead/Lesen),
**W**(rite/Schreiben, Anlegen), **E**(dit/Ändern) nebeneinander. **„Ändern“
deckt Löschen überall mit ab** — es gibt bewusst kein eigenes
Löschen-Recht. Eine vierte Spalte **D** existiert nur bei Clients und
bedeutet dort **nicht** Löschen, sondern *PoE-Neustart über das
Dashboard-Popup auslösen* (automatische Neustarts bei Ausfall über
`poe.sh` sind davon komplett unabhängig und laufen immer). Für Zeilen/Spalten
ohne passendes Recht (z.B. „W“ beim Bereich selbst, oder „D“ bei
Switche/Zugangsdaten/Benutzer/Gruppen) wird eine ausgegraute, nicht
anklickbare Checkbox angezeigt statt die Zelle wegzulassen:
```
Geräte (gesamt) R
├─ Clients R · W · E · D (D = PoE-Neustart über Dashboard)
├─ Switche R · W · E
└─ Zugangsdaten R · W · E
Logs (gesamt) R
├─ Live R
└─ Änderungen R
Einstellungen (gesamt) R
├─ Benutzer R · W · E
├─ Gruppen R · W · E
├─ Systemeinstellungen R · E
└─ Im-/Export R (Export) · E (Import)
```
Das jeweilige „Bereich an/aus“-Recht (`devices_group.view` /
`logs_group.view` / `settings_group.view`, die „(gesamt)“-Zeile) wirkt als
**Kill-Switch**: ist es für eine Gruppe nicht gesetzt, greift kein einziges
Recht darunter mehr — selbst wenn z.B. `devices.view` einzeln noch angehakt
ist. So lässt sich ein ganzer Bereich mit einem Klick sperren, ohne jedes
Unterrecht einzeln zurücknehmen zu müssen (`User.has_permission()` in
`app.py`). Auf der Gruppen-Seite sind die Unterpunkt-Zeilen einer Tabelle per
JavaScript so lange gesperrt (ausgegraut, nicht anklickbar), bis die
„(gesamt)“-Zeile angehakt ist.
### Systemgruppen
Zwei Gruppen sind fest und **weder umbenennbar noch in ihren Rechten
änderbar** (auch nicht durch Admins über die UI) — Mitgliedschaft bleibt bei
beiden frei verwaltbar:
- **Admin** — virtuell (kein echter `groups`-Datensatz), intern über den
`is_admin`-Schalter der Benutzer gesteuert, immer alle Rechte. Mindestens
ein Admin muss bestehen bleiben (serverseitig erzwungen).
- **Benutzer** — die Standardgruppe (`is_system`-Flag), der jeder neu
angelegte Nicht-Admin automatisch zugeordnet wird. Fester Rechtesatz:
Geräte-Bereich + Clients/Switche lesen, Logs-Bereich + Live-Log lesen.
Alle anderen Gruppen sind vom jeweiligen Rechteinhaber (`groups.edit`, das
auch Anlegen und Löschen von Gruppen umfasst) frei konfigurierbar.
### Eskalationsschutz
Da jetzt auch **Benutzer-** und **Gruppenverwaltung** delegierbar sind (z.B.
eine Gruppe mit nur `users.edit`, ohne Admin zu sein), gelten zusätzlich zu
den granularen Rechten diese fest einprogrammierten Schranken, unabhängig
davon, was eine Gruppe an Rechten hat:
- Ein Benutzer als Admin anlegen/dazu machen (`group_id=admin` bei
Anlegen/Zuweisen) bleibt echten Admins vorbehalten.
- Ein bestehendes Admin-Konto bearbeiten, löschen oder ihm die Gruppe ändern
bleibt echten Admins vorbehalten.
- Wer Admin ist, wird ausschließlich über „Admin-Mitglieder verwalten“ auf
der Gruppen-Seite gesteuert — bleibt admin-exklusiv, unabhängig von
`groups.edit`.
Sowohl das Anzeigen jeder Seite als auch jede einzelne Aktion (Buttons,
Formulare) ist an das jeweilige Recht gekoppelt — im Frontend ausgeblendet
**und** im Backend serverseitig durchgesetzt, unabhängig vom Frontend.
Datenmodell: `groups` (inkl. `is_default`- und `is_system`-Flag),
`group_permissions` (Gruppe → Recht), `user_groups` (Benutzer → Gruppe).
Bestehende Datenbanken werden beim App-Start automatisch migriert
(`_ensure_schema()` in `app.py`): `devices.toggle``devices.edit`,
`switches.*` → gespiegelte `credentials.*`, nachträgliches Setzen der neuen
Bereichs-Rechte für bereits vergebene Unterrechte, sowie (spätere Revision)
sämtliche `*.delete`-Rechte → `*.edit` (Löschen wurde nachträglich überall in
Ändern gefaltet). Jede dieser Migrationen läuft **nur einmalig** über einen
eigenen Guard in der `settings`-Tabelle, damit z.B. ein bewusst deaktiviertes
Bereichs-Recht nicht bei jedem Neustart automatisch wieder gesetzt wird.
## Zugangsdaten (wiederverwendbare SSH-Logins)
Switch-Zugangsdaten (Username + verschlüsseltes Passwort) sind eine eigene
Entität (`credentials`-Tabelle) statt direkt am Switch zu hängen — mehrere
Switche mit demselben Login teilen sich einen Eintrag. Beim Anlegen/Bearbeiten
eines Switches lässt sich per Dropdown ein bestehender Eintrag auswählen oder
über „+ Neue Zugangsdaten anlegen“ direkt im selben Formular ein neuer
anlegen, ohne vorher zur eigenen **Zugangsdaten**-Seite wechseln zu müssen.
Löschen ist nur möglich, wenn kein Switch mehr auf den Eintrag verweist.
Bestehende Datenbanken aus einer älteren Version (Username/Passwort direkt
am Switch) werden beim ersten Start automatisch migriert.
Jeder Switch kann außerdem einen individuellen **SSH-Port** hinterlegen
(Feld „SSH-Port“ beim Anlegen/Bearbeiten) — bleibt er leer, wird überall
automatisch **Port 22** angenommen (`SWITCH_DEFAULT_SSH_PORT` in `app.py`).
Der Port wird konsistent an allen drei Stellen verwendet, an denen die App
sich per SSH mit einem Switch verbindet: dem Web-Terminal (Verbindungstest),
`generate_ips.py` (liefert ihn als eigenes Feld an `poe.sh`) und den
`expect`-Aufrufen in `poe.sh` selbst (`ssh -p <port> ...`) für automatische
wie manuelle PoE-Neustarts.
## Änderungslog
Jede Anlage, Bearbeitung, Löschung sowie jedes Aktivieren/Deaktivieren von
Geräten, Switchen, Zugangsdaten, Benutzern und Gruppen wird in der Tabelle
`audit_log` protokolliert (Zeitpunkt, Benutzer, Aktion, Ziel, Details) —
einsehbar unter **Logs → Änderungen** (Recht `logs_activity.view`). Zusätzlich merken
sich Geräte und Switche direkt am Datensatz (`last_modified_by`,
`last_modified_at`), wer sie zuletzt geändert hat, damit man das nicht erst
im Log nachschlagen muss.
PoE-Neustarts werden **bewusst nicht** im Änderungslog erfasst — die stehen
bereits im Live-Log von `poe.sh`. Eine Ausnahme ist die Änderung des
Prüfintervalls selbst (eine Einstellungsänderung, kein Neustart), die wird
mitprotokolliert.
## Import/Export
Unter **Einstellungen** lassen sich Geräte, Switche und Zugangsdaten
(inkl. entschlüsselter Passwörter) als ein einziges JSON-Bundle exportieren.
Das Bundle wird mit einem aus einer selbstgewählten Passphrase abgeleiteten
Schlüssel verschlüsselt (PBKDF2-HMAC-SHA256 + Fernet) — unabhängig vom
`fernet.key` dieser Installation, damit die Datei auf einer komplett neuen
Umgebung (mit eigenem, neu generiertem `fernet.key`) wieder eingelesen
werden kann. Gedacht für einen Umzug auf einen neuen Server.
Beim Import wird dieselbe Passphrase erneut abgefragt; Zugangsdaten werden
nach Name, Switche nach Hostname und Geräte nach MAC-Adresse abgeglichen —
bestehende Einträge mit gleichem Schlüssel werden aktualisiert, neue werden
angelegt. Switche referenzieren ihre Zugangsdaten dabei über den Namen (nicht
die interne ID), damit der Import auch bei abweichender ID-Vergabe auf dem
Zielsystem korrekt verknüpft.
## Eigenes Konto & Profilbild
Das Zahnrad-Symbol neben dem eigenen Namen unten in der Sidebar führt zu
**„Mein Konto“** (`/account`): Vor-/Nachname ändern, ein Profilbild
hochladen (PNG/JPG/GIF/WEBP) und das eigene Passwort ändern (erfordert
Eingabe des aktuellen Passworts). Das ist bewusst getrennt vom Admin-Bereich
„Benutzer“, der andere Konten verwaltet. Ist ein Profilbild hinterlegt,
erscheint es statt der Initialen sowohl in der Sidebar als auch im
Änderungslog neben dem Benutzernamen. Admins sehen auf derselben Seite
zusätzlich die Navbar-Reihenfolge (siehe unten).
## Navigation anpassen
Die Sidebar ist in drei aufklappbare Gruppen strukturiert:
- **Geräte** — Clients (die eigentlichen Devices), Switche, Zugangsdaten
- **Einstellungen** — Benutzer, Gruppen, Systemeinstellungen (Prüfintervall),
Im-/Export
- **Logs** — Live, Änderungen (Änderungslog)
Die Reihenfolge dieser drei Gruppen (und von „Dashboard“) ist für Admins
unter **Mein Konto** (Zahnrad-Symbol in der Sidebar) per Auf-/Ab-Buttons
konfigurierbar (gespeichert als JSON-Liste unter `settings.nav_order`) und
gilt global für alle Benutzer. Unabhängig von der Reihenfolge sieht jeder
Benutzer ohnehin nur, wofür er berechtigt ist — sowohl auf Gruppenebene als
auch pro Unterpunkt: hat ein Benutzer z.B. nur `devices.view`, erscheint die
Gruppe „Geräte“ nur mit dem Unterpunkt „Clients“ (ohne Switche/
Zugangsdaten); eine Gruppe verschwindet komplett, wenn kein Unterpunkt
sichtbar ist.
## Entwicklung unter WSL
Da das Projekt ausschließlich für Linux gebaut ist, empfiehlt sich die
Entwicklung direkt unter WSL2 (Ubuntu), auch wenn der Editor unter Windows
läuft — das Projektverzeichnis liegt dabei ganz normal unter
`C:\...\aruba-poe-modern` und wird aus WSL über `/mnt/c/...` erreicht:
```bash
wsl --install -d Ubuntu # einmalig, falls noch nicht vorhanden
wsl -d Ubuntu
sudo apt update && sudo apt install -y python3 python3-venv python3-pip sqlite3 expect openssh-client
cd /mnt/c/Users/<dich>/aruba-poe-modern/srv/poe_manager
python3 -m venv venv
./venv/bin/pip install -r requirements.txt
./venv/bin/python create_db.py
./venv/bin/python create_admin.py
./venv/bin/python app.py # http://localhost:5000 — via WSL2-Portweiterleitung
# auch aus dem Windows-Browser erreichbar
```
**Hinweis zu `rpi-check.service`:** Der „Speichern & Service neustarten“-Button
unter Systemeinstellungen sowie das „Jetzt prüfen“-Icon funktionieren nur,
wenn `rpi-check.service` tatsächlich als echter systemd-Service läuft (viele
aktuelle WSL2-Installationen unterstützen systemd, siehe `wsl.conf`
`[boot] systemd=true`). Für lokale Tests reicht es, `etc/systemd/system/
rpi-check.service` sowie `usr/local/bin/custom/poe.sh` per Symlink an ihre
Produktivpfade (`/etc/systemd/system/`, `/usr/local/bin/custom/`) zu legen
und `/srv/poe_manager` auf den Checkout zu verlinken, dann `systemctl
daemon-reload && systemctl enable --now rpi-check.service`. Ohne systemd
(z.B. `systemctl` nicht vorhanden) scheitert der Neustart-Aufruf lautlos —
das Intervall in der DB wird trotzdem aktualisiert, greift dann aber erst
nach einem manuellen Neustart des Prozesses.
## Lokal starten (Entwicklung)
```bash
cd srv/poe_manager
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
python create_db.py # legt sqlite.db + Tabellen an
python create_admin.py # legt ersten Admin-Benutzer an
python app.py # http://localhost:5000
```
## Produktiv-Deployment (auf der Ziel-VM, App landet unter `/srv/poe_manager`)
Das Repo wird bewusst **nicht** direkt nach `/srv/poe_manager` geklont —
`install.sh` kopiert (`rsync`) den Inhalt von `srv/poe_manager/` aus dem Repo
gezielt dorthin und lässt Quell-Checkout und Zielverzeichnis damit sauber
getrennt (kein doppeltes `/srv/poe_manager/srv/poe_manager/…`):
```bash
sudo git clone <dieses-repo> /opt/aruba-poe-modern-src
cd /opt/aruba-poe-modern-src
sudo bash install.sh
sudo /srv/poe_manager/venv/bin/python3 /srv/poe_manager/create_admin.py
```
Nach der Installation liegt die laufende App vollständig unter
**`/srv/poe_manager`** (so wie es `poe_web.service`, `rpi-check.service` und
`POE_SCRIPT`/`POE_DB_PATH` als Default erwarten) — `/opt/aruba-poe-modern-src`
wird nur als Checkout-Quelle für `install.sh` gebraucht und kann danach
liegen bleiben (für spätere Updates: `git pull` dort, dann `install.sh`
erneut ausführen) oder gelöscht werden.
`install.sh` installiert Systempakete, richtet die venv ein, initialisiert die
Datenbank (falls nicht vorhanden), kopiert `poe.sh` nach
`/usr/local/bin/custom/`, registriert die systemd-Units
(`poe_web.service`, `rpi-check.service`, `rpi-check-restart.timer`) und
konfiguriert nginx als Reverse-Proxy auf Port 80.
### Relevante Umgebungsvariablen (optional)
| Variable | Zweck | Default |
|-----------------------|------------------------------------------|---------------------------------------|
| `POE_DB_PATH` | Pfad zur SQLite-DB | `<app-dir>/sqlite.db` |
| `POE_SCRIPT` | Pfad zum PoE-Restart-Skript | `/usr/local/bin/custom/poe.sh` |
| `POE_LOG_GLOB` | Glob-Pattern der Check-Logs | `/var/log/rpi-*.log` |
| `POE_FERNET_KEY` | Pfad zum Fernet-Schlüssel | `<app-dir>/fernet.key` (auto-erzeugt)|
| `POE_KNOWN_HOSTS` | Pfad zur SSH-known_hosts-Datei des Terminals| `<app-dir>/known_hosts` (auto-erzeugt)|
| `POE_SECRET_KEY` | Flask Secret Key (überschreibt Datei) | zufällig, in `secret.key` gespeichert|
| `PORT`, `FLASK_DEBUG` | Flask-Serverport / Debug-Modus | `5000` / aus |
## Browser-SSH-Terminal (Switch-Verbindungstest)
„Verbindung testen“ im Switch-Modal öffnet eine WebSocket-Verbindung zu
`/ws/ssh_terminal`. Serverseitig baut **paramiko** eine echte SSH-Sitzung
zum Switch auf (inkl. interaktiver Host-Key-Bestätigung beim Erstkontakt,
danach dauerhaft in `known_hosts` gemerkt) und reicht eine interaktive Shell
1:1 an ein xterm.js-Terminal im Browser durch. Die Zugangsdaten aus dem
Formular werden direkt für die SSH-Authentifizierung verwendet (nicht als
Tastatureingabe in die Shell getippt) — Erfolg/Fehlschlag der Anmeldung wird
sofort und eindeutig im Terminal angezeigt. Ein Klick auf „Passwort
einfügen“ sendet den aktuell im Formular eingetragenen Wert zusätzlich als
Tastatureingabe (z.B. für ein `sudo`-Passwort in der geöffneten Sitzung).
Voraussetzungen:
- Läuft auf **jedem Betriebssystem** (paramiko ist reines Python) — die
SSH-Verbindung wird dabei immer vom PoE-Server aus aufgebaut, nie vom
Browser/Client.
- Nur für eingeloggte **Admins** erreichbar.
- Der Flask-Entwicklungsserver muss mit `threaded=True` laufen (ist bereits
so konfiguriert) — für einen echten Produktivbetrieb empfiehlt sich
ohnehin ein WSGI-Server mit WebSocket-Unterstützung (z.B. Gunicorn mit
`gevent`- oder `eventlet`-Worker) statt des eingebauten Dev-Servers.
- Da `poe_web.service` (wie im Original) als `root` läuft, kann über dieses
Terminal im Prinzip jeder erreichbare Host per SSH kontaktiert werden —
bewusst so gewählt, da Admins über die App ohnehin schon vollen Zugriff auf
alle hinterlegten Switch-Zugangsdaten haben. Für eine strengere Trennung
empfiehlt es sich, `poe_web.service` künftig unter einem eigenen,
unprivilegierten Systembenutzer laufen zu lassen (im Original wie auch hier
aktuell `root`, um Änderungen am produktiven Setup nicht zu vergrößern).
## Projektstruktur
```
srv/poe_manager/ Flask-App (Backend + Templates + Static-Assets)
usr/local/bin/custom/ poe.sh — Ping-Check & PoE-Restart per SSH (expect)
etc/systemd/system/ Service-/Timer-Units (Web-App + Check-Dienst)
etc/nginx/ Reverse-Proxy-Konfiguration
install.sh Produktiv-Installer
```