Rebrand to TESM: infra rename, LDAP/AD login, selective import/export, Papierkorb
- Vollständiges Rebranding "PoE Manager" -> TESM (TimEShepManager): neue Logos/Favicon (theme-aware), Sidebar/Login-Branding, Copyright-Zeile. - Infrastruktur-Umbenennung: srv/poe_manager -> srv/tesm, alle POE_*-Env-Vars -> TESM_* (POE_SCRIPT bewusst unverändert, echtes PoE-Skript), systemd Units poe_web/rpi-check* -> tesm/tesm-check*, nginx-Site, netplan/logrotate Configs, Gitea-Repo alientim/Aruba-PoE-Modern -> alientim/tesm. - LDAP/Active-Directory Same-Sign-On: Search+Bind, AD-Gruppen->App-Gruppen- Zuordnung (additiv), Konto-Sperren, AD-Vorab-Suche/-Anlage, eigene Einstellungsseite. - Selektives Import/Export (8 Kategorien, Zwei-Schritt-Vorschau) mit eigenem R/E/X-Rechtemodell (X = Export, getrennt von E = Import). - Papierkorb: Soft-Delete statt Hard-Delete für Geräte/Switche/Zugangsdaten/ Benutzer/Gruppen (AD-Nutzer ausgenommen), eigene Seite unterhalb Wartung, konfigurierbare Aufbewahrungsfrist. - rpi_ip -> ip Spalten-/Code-Umbenennung (Geräte sind längst nicht mehr auf Raspberry Pis beschränkt). - README auf aktuellen Stand gebracht.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
# PoE Manager — Modern UI Rebuild
|
||||
# TESM (TimEShepManager)
|
||||
|
||||
Webbasierte Verwaltung und Monitoring von PoE-Devices und Aruba-Switchen —
|
||||
funktionales Rebuild von [Aruba-PoE](https://gitea.int.eertmoed.net/WiS/Aruba-PoE)
|
||||
@@ -47,6 +47,9 @@ Die App ermöglicht:
|
||||
getrennt vom PoE-Neustart oben): Bulk-Update (`apt update && apt upgrade`)
|
||||
und Neustart per SSH direkt auf dem Gerät, unabhängig von jeder
|
||||
Switch-Zuordnung (siehe [Wartung](#wartung-ssh-bulk-update--neustart))
|
||||
- **Optionale LDAP/Active-Directory-Anmeldung** mit dem Domänen-Passwort,
|
||||
zusätzlich zu lokalen Konten — automatische Konto-Anlage beim ersten
|
||||
Login (siehe [LDAP / Active Directory](#ldap--active-directory-same-sign-on))
|
||||
- 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
|
||||
@@ -113,8 +116,8 @@ Die App ermöglicht:
|
||||
überarbeitet — parallele statt sequentielle Geräte-Prüfung und echte
|
||||
Erfolgskontrolle statt blind angenommenem Erfolg, siehe
|
||||
[Geräte-Prüfung](#geräte-prüfung-poesh).
|
||||
- **Aufgeräumtes Backend**: konsistente, konfigurierbare Pfade (`POE_DB_PATH`,
|
||||
`POE_SCRIPT`, `POE_LOG_GLOB`, …) statt hart codierter/inkonsistenter Pfade;
|
||||
- **Aufgeräumtes Backend**: konsistente, konfigurierbare Pfade (`TESM_DB_PATH`,
|
||||
`POE_SCRIPT`, `TESM_LOG_DIR`, …) statt hart codierter/inkonsistenter Pfade;
|
||||
Secret-Key und Fernet-Key werden beim ersten Start automatisch generiert
|
||||
statt im Repo zu liegen.
|
||||
|
||||
@@ -137,29 +140,43 @@ Ein Benutzer kann mehreren Gruppen angehören, die Rechte addieren sich
|
||||
|
||||
### Zweistufiger Rechtebaum
|
||||
|
||||
Der Baum hat genau zwei Ebenen, exakt gespiegelt an den drei Sidebar-Gruppen
|
||||
(Geräte/Logs/Einstellungen). Auf der Gruppen-Seite stehen sie als drei
|
||||
kompakte Spalten nebeneinander, mit einer kurzen Legende direkt darüber.
|
||||
Jede Spalte ist eine kleine Tabelle: die Kopfzeile enthält in EINER Zeile
|
||||
sowohl den Bereichsnamen mit seinem Kästchen (dem Kill-Switch, dicht
|
||||
nebeneinander statt an den Rand gestellt) als auch die Buchstaben-Spalten
|
||||
**R**(ead/Lesen), **W**(rite/Schreiben, Anlegen), **E**(dit/Ändern) — nur
|
||||
die Rechte, die dieser Bereich tatsächlich nutzt. Darunter je eine
|
||||
eingerückte Zeile pro Unterpunkt. **„Ändern“ deckt Löschen überall mit
|
||||
ab** — es gibt bewusst kein eigenes Löschen-Recht. Eine vierte Spalte **D**
|
||||
gibt es nur bei Geräte → Clients, und sie 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 ohne passendes Recht (z.B. „D“ bei Switche/
|
||||
Zugangsdaten/Benutzer/Gruppen) wird eine ausgegraute, nicht anklickbare
|
||||
Checkbox angezeigt statt die Zelle wegzulassen:
|
||||
Der Baum hat genau zwei Ebenen, exakt gespiegelt an den drei
|
||||
Rechte-Bereichen der Gruppen-Seite (Geräte/Logs/Einstellungen) — das ist
|
||||
**unabhängig** von der Sidebar-Gliederung: „Benutzer“ und „Gruppen“ stehen
|
||||
in der Sidebar zwar als eigene, flache Top-Level-Punkte (siehe
|
||||
[Navigation anpassen](#navigation-anpassen)), gehören rechtlich aber
|
||||
weiterhin zum Bereich „Einstellungen“. Auf der Gruppen-Seite stehen die drei
|
||||
Bereiche als drei kompakte Spalten nebeneinander, mit einer kurzen Legende
|
||||
direkt darüber. Jede Spalte ist eine kleine Tabelle: die Kopfzeile enthält
|
||||
in EINER Zeile sowohl den Bereichsnamen mit seinem Kästchen (dem
|
||||
Kill-Switch, dicht nebeneinander statt an den Rand gestellt) als auch die
|
||||
Buchstaben-Spalten **R**(ead/Lesen), **W**(rite/Schreiben, Anlegen),
|
||||
**E**(dit/Ändern) — nur die Rechte, die dieser Bereich tatsächlich nutzt.
|
||||
Darunter je eine eingerückte Zeile pro Unterpunkt. **„Ändern“ deckt Löschen
|
||||
überall mit ab** — es gibt bewusst kein eigenes Löschen-Recht (stattdessen
|
||||
landet ein gelöschter Eintrag im [Papierkorb](#papierkorb)). Zwei Bereiche
|
||||
haben eine zusätzliche, bereichsspezifische vierte Spalte: **D** nur bei
|
||||
Geräte → Clients bedeutet **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); **X** nur bei
|
||||
Einstellungen → Im-/Export bedeutet *Export ausführen* — bewusst getrennt
|
||||
von E (Import), da die Export-Datei Passwörter im Klartext enthält (siehe
|
||||
[Import/Export](#importexport)). Checken einer W/E/D/X-Zelle einer Zeile
|
||||
hakt automatisch auch deren R an, und Abwählen von R wählt automatisch auch
|
||||
die übrigen Spalten derselben Zeile ab (R ist logisch Voraussetzung für
|
||||
alles andere). Für Zeilen ohne passendes Recht (z.B. „D“ bei Switche/
|
||||
Zugangsdaten/Benutzer/Gruppen, oder „X“ bei allem außer Im-/Export) wird
|
||||
eine ausgegraute, nicht anklickbare Checkbox angezeigt statt die Zelle
|
||||
wegzulassen:
|
||||
|
||||
```
|
||||
☑ Geräte R W E D ☑ Logs R W E ☑ Einstellungen R W E
|
||||
Clients ☑ ☑ ☑ ☑ Live ☑ ☑ ☑ Benutzer ☑ ☑ ☑
|
||||
Switche ☑ ☑ ☑ ▢ Änd. ☑ ☑ ☑ Gruppen ☑ ☑ ☑
|
||||
Zugangsd. ☑ ☑ ☑ ▢ Systemeinst. ☑ ▢ ☑
|
||||
Im-/Export ☑ ▢ ☑
|
||||
☑ Geräte R W E D ☑ Logs R W E ☑ Einstellungen R W E X
|
||||
Clients ☑ ☑ ☑ ☑ Live ☑ ☑ ☑ Benutzer ☑ ☑ ☑ ▢
|
||||
Switche ☑ ☑ ☑ ▢ Änderungen☑ ☑ ☑ Gruppen ☑ ☑ ☑ ▢
|
||||
Zugangsd. ☑ ☑ ☑ ▢ Kea-DHCP ☑ ☑ ☑ Systemeinst. ☑ ▢ ☑ ▢
|
||||
Wartung ☑ ▢ ☑ ▢ LDAP ☑ ▢ ☑ ▢
|
||||
DHCP ☑ ▢ ☑ ▢
|
||||
Im-/Export ☑ ▢ ☑ ☑
|
||||
```
|
||||
|
||||
Das jeweilige „Bereich sichtbar“-Recht (`devices_group.view` /
|
||||
@@ -217,6 +234,136 @@ sämtliche `*.delete`-Rechte → `*.edit` (Löschen wurde nachträglich überall
|
||||
eigenen Guard in der `settings`-Tabelle, damit z.B. ein bewusst deaktiviertes
|
||||
Bereichs-Recht nicht bei jedem Neustart automatisch wieder gesetzt wird.
|
||||
|
||||
## LDAP / Active Directory (Same Sign-On)
|
||||
|
||||
Optionale Anmeldung mit dem Domänen-Passwort, zusätzlich zu lokalen Konten
|
||||
— auf der eigenen Seite **Einstellungen → LDAP** (Recht `settings_ldap.view`
|
||||
zum Ansehen, Speichern erfordert Admin).
|
||||
|
||||
**Wichtige Begriffsklärung, direkt aus einer Nutzerfrage entstanden:** das
|
||||
ist **"Same Sign-On"**, kein automatisches, promptloses **Single Sign-On**.
|
||||
Der Benutzer tippt sein AD-Passwort weiterhin auf der normalen Login-Seite
|
||||
ein — es wird nur bei jedem Login **live** gegen Active Directory geprüft
|
||||
statt gegen ein lokal gespeichertes Passwort. Eine Passwortänderung in AD
|
||||
greift dadurch sofort, ohne dass irgendetwas synchronisiert werden müsste.
|
||||
Echtes, promptloses Single Sign-On (der Browser meldet automatisch den
|
||||
bereits an Windows angemeldeten Benutzer an) bräuchte zusätzlich:
|
||||
- **Kerberos/SPNEGO** ("Integrated Windows Authentication"): ein SPN +
|
||||
Keytab vom AD-Admin, eine per DNS auflösbare Hostname für die App, und
|
||||
die Browser-Konfiguration der Clients (i.d.R. bereits Standard für die
|
||||
Intranet-Zone auf domänenbeigetretenen PCs) — **oder**
|
||||
- **SAML/OIDC über Entra ID (Azure AD)**, falls Microsoft 365/Entra
|
||||
vorhanden ist — oft der pragmatischere Weg zu echtem SSO als klassisches
|
||||
Kerberos.
|
||||
|
||||
Beides sind eigenständige, deutlich aufwändigere Ausbaustufen, die sich
|
||||
später auf die hier beschriebene LDAP-Anmeldung aufsetzen ließen, aber
|
||||
AD-seitige Vorarbeit (Keytab bzw. App-Registrierung) durch einen
|
||||
Domain-Admin voraussetzen.
|
||||
|
||||
### Funktionsweise (Search+Bind)
|
||||
|
||||
Beim Login wird zunächst lokal gesucht — per exaktem Benutzernamen, per
|
||||
E-Mail-Adresse, oder (case-insensitiv, nur für `auth_source='ldap'`-Konten)
|
||||
per bereits bekanntem AD-Benutzernamen. Ist das gefundene (oder noch nicht
|
||||
existierende) Konto gesperrt (`is_locked`, siehe unten) oder im Papierkorb,
|
||||
wird der Login abgelehnt, ohne AD überhaupt zu kontaktieren. Existiert kein
|
||||
lokales Konto und ist LDAP aktiviert, prüft die App die eingegebene Kennung
|
||||
(sAMAccountName **oder** UPN, z.B. sowohl `jdoe` als auch
|
||||
`jdoe@ad.eertmoed.net` funktionieren) und das Passwort per **Search+Bind**
|
||||
gegen den konfigurierten Server:
|
||||
|
||||
1. Verbindung mit dem konfigurierten **Service-Konto** aufbauen (ein
|
||||
normales, unprivilegiertes Domänenkonto reicht — es braucht nur
|
||||
Lesezugriff auf das Verzeichnis, was jedes Domänenkonto in AD
|
||||
standardmäßig hat).
|
||||
2. Den Benutzer per konfigurierbarem Attribut suchen (Standard
|
||||
`sAMAccountName` für Active Directory; für generisches LDAP z.B. `uid`).
|
||||
3. Eine **zweite, separate** Verbindung mit der gefundenen DN des Benutzers
|
||||
und dem eingegebenen Passwort aufbauen, um es zu verifizieren — robuster
|
||||
als ein direktes UPN-Bind (`user@domain`), da es nicht voraussetzt, dass
|
||||
`sAMAccountName` und der UPN-Präfix übereinstimmen.
|
||||
4. Zusätzlich wird das `userAccountControl`-Bit `ACCOUNTDISABLE` geprüft
|
||||
(auch wenn ein echter AD-DC den Bind eines deaktivierten Kontos i.d.R.
|
||||
ohnehin schon selbst ablehnt) — zusätzliche Absicherung.
|
||||
|
||||
Die **Base-DN** wird standardmäßig automatisch per RootDSE
|
||||
(`defaultNamingContext`) ermittelt, kann aber manuell überschrieben werden
|
||||
(z.B. bei mehreren Domänen in einer Gesamtstruktur).
|
||||
|
||||
Bei erfolgreichem erstem Login wird automatisch ein lokales Konto angelegt
|
||||
(`auth_source='ldap'`, kein Admin). Das gespeicherte Passwort dieses Kontos
|
||||
ist nur ein **nie geprüfter Platzhalter-Hash** — bei jedem weiteren Login
|
||||
wird live gegen AD verifiziert, nie gegen die lokale DB. Benutzernamen
|
||||
werden dabei case-insensitiv abgeglichen (nur für `auth_source='ldap'`-
|
||||
Konten, wirkt sich nie auf lokale Konten aus), damit z.B. "JDoe" und "jdoe"
|
||||
nicht zwei verschiedene lokale Konten erzeugen.
|
||||
|
||||
**Gruppen-Zuordnung (AD-Gruppe → App-Gruppe).** Auf der LDAP-Einstellungsseite
|
||||
lässt sich jeder AD-Gruppe (per DN) genau eine App-Gruppe zuordnen. Ist der
|
||||
Benutzer beim Login Mitglied mehrerer zugeordneter AD-Gruppen, erhält er
|
||||
**additiv** die Rechte aller passenden App-Gruppen (dieselbe
|
||||
Vereinigungslogik wie bei mehrfacher lokaler Gruppenmitgliedschaft). Passt
|
||||
keine der konfigurierten Zuordnungen, greift ersatzweise eine konfigurierbare
|
||||
**Standardgruppe** (sonst die System-Standardgruppe „Benutzer“) — es sei denn,
|
||||
eine der AD-Gruppen ist explizit der internen Admin-Zuordnung zugewiesen,
|
||||
dann wird das Konto automatisch zum Admin. Die Zuordnung wird bei **jedem**
|
||||
Login neu ausgewertet, nicht nur beim ersten — Änderungen an
|
||||
AD-Gruppenmitgliedschaften wirken sich also ohne manuellen Eingriff auf die
|
||||
nächste Anmeldung aus.
|
||||
|
||||
**Konten sperren.** Ein Admin kann jedes Konto (lokal wie AD) in „Benutzer“
|
||||
per Schalter sperren (`is_locked`) — ein gesperrtes Konto kann sich nicht
|
||||
mehr anmelden, unabhängig davon, ob das AD-Passwort weiterhin gültig ist.
|
||||
Nützlich, um jemandem ohne Eingriff in AD selbst kurzfristig den Zugriff auf
|
||||
die App zu entziehen.
|
||||
|
||||
**Vorab-Anlage aus Active Directory** („Benutzer" → „Aus Active Directory
|
||||
hinzufügen"): eine Live-Suche (Substring, ab 2 Zeichen) im Verzeichnis,
|
||||
gefiltert um bereits lokal bekannte Benutzernamen. So kann ein Admin einer
|
||||
Person schon **vor** ihrem ersten Login eine Gruppe zuweisen, statt sich auf
|
||||
die Standardgruppe zu verlassen — das Konto landet dabei sofort mit
|
||||
`auth_source='ldap'` und demselben Platzhalter-Passwort-Mechanismus in der
|
||||
DB, ganz so, als hätte die Person sich bereits einmal angemeldet.
|
||||
|
||||
**Lokale Konten bleiben vollständig erhalten** — der lokale Login-Pfad ist
|
||||
unverändert, LDAP kommt nur als zusätzlicher Weg für Benutzernamen ohne
|
||||
lokales Konto dazu. Wichtig als Fallback (Break-Glass-Zugang, falls
|
||||
AD/LDAP mal nicht erreichbar ist) und für den allerersten
|
||||
Admin-Bootstrap (`create_admin.py`).
|
||||
|
||||
Für AD-Konten wird die Passwort-Änderung sowohl unter „Mein Konto" als auch
|
||||
im Admin-Bereich „Benutzer" ausgeblendet bzw. serverseitig ignoriert (ein
|
||||
kleines "AD"-Symbol markiert sie in der Benutzerliste) — das Passwort wird
|
||||
ausschließlich in Active Directory verwaltet.
|
||||
|
||||
**Löschen eines AD-Kontos** in „Benutzer" entfernt nur das lokale
|
||||
Schatten-Konto — **AD-Konten landen dabei bewusst NIE im Papierkorb**
|
||||
(anders als lokale Konten, siehe [Papierkorb](#papierkorb)), da sie sich bei
|
||||
jedem gültigen Login ohnehin automatisch wieder anlegen würden und ein
|
||||
"gelöscht, aber weiter nutzbar"-Zwischenzustand hier keinen Sinn ergibt.
|
||||
Meldet sich die Person danach erneut mit gültigen AD-Zugangsdaten an, wird
|
||||
automatisch wieder ein (neues, rechteloses) Konto angelegt. Um jemandem den
|
||||
Zugriff wirklich zu entziehen, sperrt man das Konto entweder direkt in der
|
||||
App (siehe oben) oder deaktiviert es in AD selbst (blockiert dann auch den
|
||||
LDAP-Bind). Aus demselben Grund werden AD-Konten beim
|
||||
[Export](#importexport) grundsätzlich nie mit exportiert — nur lokale
|
||||
Konten landen in der Export-Datei.
|
||||
|
||||
**Sicherheitshinweis**: ohne LDAPS/StartTLS wird das Passwort unverschlüsselt
|
||||
über das Netzwerk übertragen — nur für interne Tests in einem vertrauenswürdigen
|
||||
Netz geeignet. Vor Produktivbetrieb sollte LDAPS auf dem Domain Controller
|
||||
eingerichtet und in den Einstellungen aktiviert werden (das Server-Zertifikat
|
||||
wird dafür noch nicht validiert — reine Transportverschlüsselung).
|
||||
|
||||
Live gegen einen echten Active-Directory-Domain-Controller verifiziert:
|
||||
automatische Base-DN-Ermittlung, "Verbindung testen", erfolgreicher
|
||||
Erst-Login mit automatischer Konto-Anlage und korrekter
|
||||
Standardgruppen-Zuweisung, Ablehnung bei falschem Passwort, erneuter Login
|
||||
verwendet das bereits angelegte Konto (keine Duplikate), unveränderter
|
||||
lokaler Admin-Login, sowie dass das Bind-Passwort nie im Klartext im
|
||||
gerenderten HTML landet.
|
||||
|
||||
## Zugangsdaten (wiederverwendbare SSH-Logins)
|
||||
|
||||
Zugangsdaten (Username + verschlüsseltes Passwort) sind eine eigene Entität
|
||||
@@ -297,7 +444,7 @@ letzten Aktion, per Klick ein-/ausblendbar) wird per Polling (`/maintenance/stat
|
||||
alle 2s solange etwas läuft, sonst alle 8s) aktualisiert, ohne die Seite neu
|
||||
zu laden. Der Job-Status lebt bewusst nur im Prozessspeicher (wie beim
|
||||
SSH-Terminal auch keine Sitzung persistiert wird) — ein Neustart von
|
||||
`poe_web.service` verwirft nur die Anzeige, nicht die auf dem Zielgerät
|
||||
`tesm.service` verwirft nur die Anzeige, nicht die auf dem Zielgerät
|
||||
bereits laufende Aktion selbst. Ein abgeschlossener **erfolgreicher** Job
|
||||
wird zusätzlich bei jedem (erneuten) Laden der Seite zurückgesetzt (Status
|
||||
wieder „Noch keine Aktion“) — ein Erfolg ist nur so lange relevant, wie man
|
||||
@@ -497,7 +644,7 @@ wurde korrekt mit der „Ohne Reservierung“-Markierung angezeigt.
|
||||
|
||||
## Geräte-Prüfung (`poe.sh`)
|
||||
|
||||
Der Hintergrund-Check-Dienst (`rpi-check.service`, ruft `poe.sh` ohne
|
||||
Der Hintergrund-Check-Dienst (`tesm-check.service`, ruft `poe.sh` ohne
|
||||
Argument auf) prüft in einer Endlosschleife alle Geräte per Ping und
|
||||
startet bei Ausfall automatisch den PoE-Port neu, über den ein Gerät am
|
||||
zugeordneten Switch hängt (`expect`, siehe unten). Zwei Korrekturen nach
|
||||
@@ -543,7 +690,7 @@ fälschlich als 43× „PoE deaktiviert“/„PoE aktiviert“ geloggt.
|
||||
## Netzwerkeinstellungen des Hosts (Systemeinstellungen)
|
||||
|
||||
Eigene, klar getrennte Funktion von obigem DHCP-*Server*: hier geht es um
|
||||
die Netzwerkkonfiguration des Hosts, auf dem der PoE Manager selbst läuft
|
||||
die Netzwerkkonfiguration des Hosts, auf dem TESM selbst läuft
|
||||
(IP-Adresse, DNS-Server, DHCP-Client vs. statisch, Hostname) — nicht um
|
||||
Adressen, die er an andere Geräte vergibt. Der Hostname wird separat und
|
||||
risikolos über `hostnamectl set-hostname` gesetzt (kein Revert-Timer nötig,
|
||||
@@ -578,7 +725,7 @@ da eine Änderung nicht die Erreichbarkeit kappt).
|
||||
IP/Prefix/Gateway, sowie unabhängig davon eigene DNS-Server (funktioniert
|
||||
in beiden Modi — z.B. IP per DHCP, aber feste DNS-Server). Bei netplan
|
||||
landet die Änderung in einer eigenen, niedrig priorisierten Override-Datei
|
||||
(`/etc/netplan/90-poe-manager.yaml`) statt in der vom Distributor bzw.
|
||||
(`/etc/netplan/90-tesm.yaml`) statt in der vom Distributor bzw.
|
||||
cloud-init mitgelieferten — netplan wendet Dateien alphabetisch an, spätere
|
||||
Werte gewinnen pro Schlüssel, damit bleibt die Original-Datei unangetastet.
|
||||
- **Sicherheitsnetz** (wie `netplan try`): vor jeder Änderung wird die
|
||||
@@ -635,18 +782,18 @@ Monat Historie):
|
||||
|
||||
| Log | Datei | Einsehbar unter |
|
||||
|-----|-------|------------------|
|
||||
| Live (Ping-Check/PoE-Neustart) | `/var/log/poe-manager/live.log` | Logs → Live |
|
||||
| Änderungen (Audit-Trail) | `/var/log/poe-manager/changes.log` | Logs → Änderungen (aus der DB) |
|
||||
| App (Fehler/Warnungen der Flask-App) | `/var/log/poe-manager/app.log` | nur Datei, keine eigene Seite |
|
||||
| Live (Ping-Check/PoE-Neustart) | `/var/log/tesm/live.log` | Logs → Live |
|
||||
| Änderungen (Audit-Trail) | `/var/log/tesm/changes.log` | Logs → Änderungen (aus der DB) |
|
||||
| App (Fehler/Warnungen der Flask-App) | `/var/log/tesm/app.log` | nur Datei, keine eigene Seite |
|
||||
| Kea-DHCP | `/var/log/kea/kea-dhcp4.log` | Logs → Kea-DHCP (Recht `logs_kea.view`) |
|
||||
|
||||
- **Live-Log ist jetzt eine einzige, durchgehend fortgeschriebene Datei**
|
||||
statt vorher bei jedem (auch täglich per `rpi-check-restart.timer`
|
||||
statt vorher bei jedem (auch täglich per `tesm-check-restart.timer`
|
||||
automatisch ausgelösten) Dienst-Neustart ein neues `rpi-<timestamp>.log`
|
||||
— `poe.sh` räumt nicht mehr selbst nach 30 Tagen auf, das übernimmt jetzt
|
||||
zentral logrotate. Bestehende alte `rpi-*.log`-Dateien werden beim ersten
|
||||
Start dieser Version einmalig aufgeräumt (Migration).
|
||||
- **Kea-DHCP-Log liegt bewusst NICHT unter `/var/log/poe-manager/`**: Keas
|
||||
- **Kea-DHCP-Log liegt bewusst NICHT unter `/var/log/tesm/`**: Keas
|
||||
AppArmor-Profil (`/etc/apparmor.d/usr.sbin.kea-dhcp4`) erlaubt
|
||||
Schreibzugriff exklusiv auf den Dateinamen `/var/log/kea/kea-dhcp4.log`
|
||||
(kein Verzeichnis-Wildcard) — ein anderer Pfad wird mit `DENIED ... mknod
|
||||
@@ -660,14 +807,14 @@ Monat Historie):
|
||||
funktioniert ohne jede Kooperation des schreibenden Prozesses und läuft
|
||||
als root, ist also auch von Keas AppArmor-Profil nicht betroffen.
|
||||
- **App-Log** (`app.log`) fängt Fehler/Warnungen der Flask-App ab (vorher
|
||||
nur im journalctl-Output von `poe_web.service` sichtbar, ohne von der App
|
||||
nur im journalctl-Output von `tesm.service` sichtbar, ohne von der App
|
||||
selbst kontrollierbare Aufbewahrung) — bewusst nur `WARNING` und höher,
|
||||
kein Access-/Request-Rauschen.
|
||||
- **Zeitzone** (Systemeinstellungen, zusammen mit dem Hostnamen in der
|
||||
Kachel „Host“): setzt die System-Zeitzone per `timedatectl
|
||||
set-timezone`, damit alle vier Logs sowie das Änderungslog die
|
||||
tatsächliche lokale Zeit statt UTC zeigen. Wirkt über `time.tzset()`
|
||||
sofort für die bereits laufende `poe_web.service`-Instanz (kein
|
||||
sofort für die bereits laufende `tesm.service`-Instanz (kein
|
||||
Neustart nötig) — `poe.sh`/`date` liest die Zeitzone ohnehin bei jedem
|
||||
Prüf-Durchlauf frisch, da es ein neuer Prozess pro Durchlauf ist.
|
||||
- **Systemeinstellungen neu sortiert**: Host (Hostname + Zeitzone in einer
|
||||
@@ -682,20 +829,85 @@ bereits den neuen Zeitstempel.
|
||||
|
||||
## Import/Export
|
||||
|
||||
Unter **Einstellungen** lassen sich Geräte, Switche und Zugangsdaten
|
||||
(inkl. entschlüsselter Passwörter) als ein einziges JSON-Bundle exportieren.
|
||||
Unter **Einstellungen → Im-/Export** lassen sich acht Kategorien einzeln
|
||||
auswählen und als ein JSON-Bundle exportieren: Geräte, Switche, Zugangsdaten
|
||||
(inkl. entschlüsselter Passwörter), Benutzer, Gruppen, LDAP-Einstellungen
|
||||
(inkl. Gruppenzuordnungen), DHCP-Konfiguration und Logs. **AD/LDAP-
|
||||
Benutzerkonten werden dabei nie mitexportiert** — nur lokale Konten landen
|
||||
in der Datei, siehe [LDAP-Kapitel](#ldap--active-directory-same-sign-on).
|
||||
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.
|
||||
werden kann. Gedacht für einen Umzug auf einen neuen Server oder ein
|
||||
selektives Backup einzelner Kategorien.
|
||||
|
||||
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.
|
||||
**Zweistufiger Import.** Datei hochladen + Passphrase eingeben lädt das
|
||||
Bundle serverseitig nur kurzzeitig in den Speicher (Token-basiert, läuft
|
||||
nach einer Weile automatisch ab) und zeigt eine **Vorschau**: nur die
|
||||
Kategorien, die tatsächlich in der Datei enthalten sind, jeweils mit Anzahl
|
||||
der Einträge. Erst danach wählt man aus, welche der vorhandenen Kategorien
|
||||
tatsächlich übernommen werden sollen — ein Import kann also z.B. nur die
|
||||
Geräte aus einer Datei ziehen, ohne versehentlich auch Benutzer oder
|
||||
LDAP-Einstellungen zu überschreiben. Abgeglichen wird pro Kategorie nach dem
|
||||
jeweiligen natürlichen Schlüssel (Zugangsdaten nach Name, Switche nach
|
||||
Hostname, Geräte nach MAC-Adresse, Benutzer nach Benutzername, Gruppen nach
|
||||
Name) — bestehende Einträge werden aktualisiert, neue 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.
|
||||
|
||||
**Berechtigungen.** Die Im-/Export-Seite hat drei eigene Rechte: **R**
|
||||
(`settings_importexport.view`) zum Ansehen der Seite (Standardbedeutung wie
|
||||
überall), **E** (`settings_importexport.edit`) zum Ausführen eines Imports,
|
||||
und **X** (`settings_importexport.export`) — eine eigene, dedizierte
|
||||
Berechtigung nur zum Ausführen eines Exports, da die Export-Datei Passwörter
|
||||
im Klartext enthält (entschlüsselt vor der eigenen Passphrasen-
|
||||
Verschlüsselung) und deshalb bewusst nicht automatisch an jeden mit
|
||||
Änderungsrecht gekoppelt ist. Admins haben ohnehin immer alle drei; für
|
||||
andere Gruppen lassen sich Import und Export unabhängig voneinander vergeben.
|
||||
Zusätzlich sind die drei Kategorien **Benutzer, Gruppen und LDAP**
|
||||
unabhängig von R/E/X grundsätzlich Admins vorbehalten (sowohl beim Export
|
||||
als auch beim Import) — ein Nicht-Admin mit E/X sieht und nutzt dort nur die
|
||||
übrigen fünf Kategorien.
|
||||
|
||||
## Papierkorb
|
||||
|
||||
Löschen ist für Geräte, Switche, Zugangsdaten, (lokale) Benutzer und
|
||||
(benutzerdefinierte) Gruppen ein **Soft-Delete** statt eines echten
|
||||
`DELETE` — der Eintrag bekommt nur einen Zeitstempel (`deleted_at`) und
|
||||
verschwindet dadurch aus allen normalen Listen, Dropdowns und
|
||||
Berechtigungsprüfungen, bleibt aber vollständig erhalten und wiederherstellbar.
|
||||
Erreichbar über einen eigenen Navigationspunkt **„Papierkorb"** direkt
|
||||
unterhalb von „Wartung" (sichtbar, sobald man für mindestens eine der fünf
|
||||
Kategorien Leserecht hat) — dort ist jede Kategorie als eigene, permissions-
|
||||
gegated Kachel mit „Wiederherstellen"- und „Endgültig löschen"-Aktion
|
||||
aufgeführt.
|
||||
|
||||
**Ausnahmen**: System-/Standardgruppen (aktuell nur „Benutzer") lassen sich
|
||||
grundsätzlich nicht löschen, landen also auch nie im Papierkorb — unverändert
|
||||
gegenüber vorher. **AD/LDAP-Benutzerkonten landen ebenfalls nie im
|
||||
Papierkorb** und werden bei „Löschen" weiterhin sofort endgültig entfernt, da
|
||||
sie sich bei jedem gültigen Login automatisch neu anlegen (siehe
|
||||
[LDAP-Kapitel](#ldap--active-directory-same-sign-on)) — ein
|
||||
Papierkorb-Zwischenzustand wäre hier bedeutungslos.
|
||||
|
||||
**Reservierter Schlüssel, solange etwas im Papierkorb liegt**: MAC-Adresse,
|
||||
Hostname, Zugangsdaten-Name, Benutzername bzw. Gruppenname eines
|
||||
gelöschten Eintrags bleiben bewusst reserviert und blockieren die
|
||||
Neuanlage eines gleichnamigen Eintrags, bis der alte entweder
|
||||
wiederhergestellt oder endgültig gelöscht (purged) wird — der Fehlertext
|
||||
beim Anlegen weist entsprechend auf den Papierkorb hin. Kind-Datensätze
|
||||
(Gruppenmitgliedschaften, Gruppenrechte, DHCP-Werte pro Gerät) werden beim
|
||||
Soft-Delete bewusst **nicht** mit entfernt, damit eine Wiederherstellung
|
||||
exakt den vorherigen Zustand zurückbringt — aufgeräumt werden sie erst beim
|
||||
**endgültigen** Löschen (Purge).
|
||||
|
||||
**Aufbewahrungsfrist**: konfigurierbar unter Systemeinstellungen
|
||||
(`trash_retention_days`, Standard 30 Tage) — abgelaufene Einträge werden
|
||||
beim Aufruf der Papierkorb-Seite automatisch endgültig gelöscht (kein
|
||||
eigener Scheduler nötig), zusätzlich gibt es dort einen Button, um
|
||||
abgelaufene Einträge sofort manuell zu bereinigen.
|
||||
|
||||
## Eigenes Konto & Profilbild
|
||||
|
||||
@@ -705,8 +917,9 @@ 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).
|
||||
Änderungslog neben dem Benutzernamen. Die Navbar-Reihenfolge (für Admins)
|
||||
liegt dagegen unter Systemeinstellungen, siehe
|
||||
[Navigation anpassen](#navigation-anpassen).
|
||||
|
||||
## Warnung bei ungespeicherten Änderungen
|
||||
|
||||
@@ -754,23 +967,28 @@ verursachen, obwohl der Code auf dem Server längst aktuell war.
|
||||
|
||||
## Navigation anpassen
|
||||
|
||||
Die Sidebar ist in drei aufklappbare Gruppen strukturiert:
|
||||
Die Sidebar besteht aus zwei aufklappbaren Gruppen und mehreren flachen
|
||||
Top-Level-Punkten dazwischen:
|
||||
|
||||
- **Geräte** — Clients (die eigentlichen Devices), Switche, Zugangsdaten,
|
||||
Wartung (SSH-Bulk-Update/-Neustart)
|
||||
- **Einstellungen** — Benutzer, Gruppen, Systemeinstellungen (Prüfintervall),
|
||||
Im-/Export
|
||||
- **Logs** — Live, Änderungen (Änderungslog)
|
||||
- **Geräte** (Gruppe) — Clients (die eigentlichen Devices), Switche,
|
||||
Zugangsdaten, Wartung (SSH-Bulk-Update/-Neustart), **Papierkorb**
|
||||
(direkt unterhalb von Wartung, siehe [Papierkorb](#papierkorb))
|
||||
- **Benutzer**, **Gruppen** — eigene, flache Top-Level-Punkte direkt nach
|
||||
„Geräte“ (bewusst nicht in einer Untergruppe versteckt)
|
||||
- **Einstellungen** (Gruppe) — Systemeinstellungen (Host/Prüfintervall/
|
||||
Navbar-Reihenfolge/Papierkorb-Aufbewahrung), LDAP, DHCP, Im-/Export
|
||||
- **Logs** (Gruppe) — Live, Änderungen, Kea-DHCP
|
||||
|
||||
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.
|
||||
Die Reihenfolge der Top-Level-Punkte **und** der Unterpunkte innerhalb jeder
|
||||
Gruppe ist für Admins unter **Systemeinstellungen** (eigene, nicht
|
||||
einklappbare Karte „Navbar-Reihenfolge") per Auf-/Ab-Buttons konfigurierbar
|
||||
(gespeichert als JSON unter `settings.nav_order` bzw.
|
||||
`settings.nav_child_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/Wartung/Papierkorb); eine Gruppe
|
||||
verschwindet komplett, wenn kein Unterpunkt sichtbar ist.
|
||||
|
||||
## Entwicklung unter WSL
|
||||
|
||||
@@ -783,7 +1001,7 @@ läuft — das Projektverzeichnis liegt dabei ganz normal unter
|
||||
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
|
||||
cd /mnt/c/Users/<dich>/aruba-poe-modern/srv/tesm
|
||||
python3 -m venv venv
|
||||
./venv/bin/pip install -r requirements.txt
|
||||
./venv/bin/python create_db.py
|
||||
@@ -792,15 +1010,15 @@ python3 -m venv venv
|
||||
# auch aus dem Windows-Browser erreichbar
|
||||
```
|
||||
|
||||
**Hinweis zu `rpi-check.service`:** Der „Speichern & Service neustarten“-Button
|
||||
**Hinweis zu `tesm-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
|
||||
wenn `tesm-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
|
||||
tesm-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
|
||||
und `/srv/tesm` auf den Checkout zu verlinken, dann `systemctl
|
||||
daemon-reload && systemctl enable --now tesm-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.
|
||||
@@ -808,7 +1026,7 @@ nach einem manuellen Neustart des Prozesses.
|
||||
## Lokal starten (Entwicklung)
|
||||
|
||||
```bash
|
||||
cd srv/poe_manager
|
||||
cd srv/tesm
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
@@ -819,23 +1037,23 @@ 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`)
|
||||
## Produktiv-Deployment (auf der Ziel-VM, App landet unter `/srv/tesm`)
|
||||
|
||||
Das Repo wird bewusst **nicht** direkt nach `/srv/poe_manager` geklont —
|
||||
`install.sh` kopiert (`rsync`) den Inhalt von `srv/poe_manager/` aus dem Repo
|
||||
Das Repo wird bewusst **nicht** direkt nach `/srv/tesm` geklont —
|
||||
`install.sh` kopiert (`rsync`) den Inhalt von `srv/tesm/` aus dem Repo
|
||||
gezielt dorthin und lässt Quell-Checkout und Zielverzeichnis damit sauber
|
||||
getrennt (kein doppeltes `/srv/poe_manager/srv/poe_manager/…`):
|
||||
getrennt (kein doppeltes `/srv/tesm/srv/tesm/…`):
|
||||
|
||||
```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
|
||||
sudo /srv/tesm/venv/bin/python3 /srv/tesm/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`
|
||||
**`/srv/tesm`** (so wie es `tesm.service`, `tesm-check.service` und
|
||||
`POE_SCRIPT`/`TESM_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.
|
||||
@@ -843,19 +1061,19 @@ 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
|
||||
(`tesm.service`, `tesm-check.service`, `tesm-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` |
|
||||
| `TESM_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|
|
||||
| `TESM_LOG_DIR` | Verzeichnis für `live.log`/`changes.log`/`app.log` | `/var/log/tesm` |
|
||||
| `TESM_FERNET_KEY` | Pfad zum Fernet-Schlüssel | `<app-dir>/fernet.key` (auto-erzeugt)|
|
||||
| `TESM_KNOWN_HOSTS` | Pfad zur SSH-known_hosts-Datei des Terminals| `<app-dir>/known_hosts` (auto-erzeugt)|
|
||||
| `TESM_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)
|
||||
@@ -881,24 +1099,24 @@ Voraussetzungen:
|
||||
ohnehin ein WSGI-Server mit WebSocket-Unterstützung (z.B. Gunicorn mit
|
||||
`gevent`- oder `eventlet`-Worker) statt des eingebauten Dev-Servers.
|
||||
- **nginx muss den WebSocket-Upgrade für `/ws/` durchreichen** (`Upgrade`/
|
||||
`Connection`-Header, siehe `etc/nginx/sites-available/poe_manager`) — ohne
|
||||
`Connection`-Header, siehe `etc/nginx/sites-available/tesm`) — ohne
|
||||
das lehnt Flask den Handshake mit `400 Bad Request` ab und das Terminal
|
||||
bleibt leer auf „Getrennt“ hängen, obwohl derselbe Aufruf direkt gegen
|
||||
Port 5000 (an nginx vorbei) funktioniert. War auf einer frisch per
|
||||
`install.sh` aufgesetzten Ubuntu-VM live reproduzierbar, da alle bisherigen
|
||||
Terminal-Tests ausschließlich direkt gegen Flask liefen, nie durch nginx.
|
||||
- Da `poe_web.service` (wie im Original) als `root` läuft, kann über dieses
|
||||
- Da `tesm.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,
|
||||
empfiehlt es sich, `tesm.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)
|
||||
srv/tesm/ 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
|
||||
|
||||
Reference in New Issue
Block a user