Vollstaendiger Neubau der Anwendung. Der vorherige Stand bleibt unveraendert
im Zweig SONNET5 erhalten.
Aufbau: apps/tesm (Anwendung), packages/tesm-core (gemeinsamer Kern),
packages/tesm-licensing (Lizenzprotokoll), deploy (Installation, systemd,
privilegierter Helfer), docs, tests. Der Lizenzserver liegt in seinem eigenen
Repository; beide Repositorien bringen die gemeinsamen Pakete mit, damit sich
jedes allein installieren laesst.
Die wichtigsten Unterschiede zum Vorgaenger, jeweils an der Stelle im Code
kommentiert, an der der Fehler entstanden ist:
* Der Webprozess laeuft unprivilegiert. Alles, was Root braucht, geht ueber
einen einzigen Helfer mit Positivlisten fuer jedes Argument.
* CSRF-Schutz ueberhaupt -- der Vorgaenger hatte keinen.
* Rechte werden serverseitig geprueft, nicht nur im Template ausgeblendet.
* Die nginx-Site wird bei jedem Lauf inhaltlich verglichen und erneuert.
* Jede erzeugte Konfiguration wird vor dem Uebernehmen geprueft (nginx, Kea).
* Kein Hostname im Lizenz-Fingerabdruck.
* Zwei Installationen auf einem Host stoeren sich nicht (eigener SITE_KEY).
* Verschachtelte Datenbankverbindungen sind ein Fehler, kein Deadlock.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Live auf POETEST reproduziert: rsync --delete im App-Verzeichnis-Schritt
schliesst venv/sqlite.db/fernet.key/secret.key/known_hosts explizit aus,
vergisst dabei aber license.json (in Phase 1 neu hinzugekommen, als
Ausschluss schlicht uebersehen). Da license.json nur auf dem Zielsystem
existiert (nie im Release-Paket enthalten, korrekt .gitignored), loescht
--delete sie bei JEDEM 'sudo ./update.sh' auf einer bereits aktivierten
Instanz kommentarlos mit -- die Lizenz war danach weg, ohne dass update.sh
oder install.sh irgendeinen Fehler gemeldet haetten.
Der Master-seitige Installer (install.sh im tesm-license-Repo) hatte
diesen Ausschluss von Anfang an korrekt gesetzt -- betraf ausschliesslich
TESM-Kundeninstanzen.
Fix: license.json zur rsync --exclude-Liste hinzugefuegt, analog zu den
anderen laufzeit-generierten Secrets.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Freigaben-Auswahl von einem Dropdown auf eine Baum-Navigation (links)
umgestellt: alle gemounteten Freigaben als Wurzelknoten, Ordner beliebig
aufklappbar. Der Pfad von der Wurzel bis zur aktuellen Position wird
serverseitig schon aufgeklappt gerendert (_fileshare_tree_ancestors),
alles andere laedt der Client per Klick lazy nach (neue Route
/fileshare/subfolders) -- kein Durchlaufen der kompletten Freigabe auf
einmal noetig. Dateien stehen bewusst nicht im Baum, nur in der
bestehenden Tabelle rechts.
- Inline-Vorschau fuer PDF (iframe), Bilder, TXT/CSV/LOG/MD/JSON (per Range-
Request in Haeppchen bis 512KB, mit Hinweis bei Abschneiden -- Erkennung
ueber den Content-Range-Header, nicht nur den 206-Status, der bei JEDEM
Range-Request auftritt) sowie Word/Excel rein clientseitig per mammoth.js
bzw. SheetJS (beide lokal vendored, kein Cloud-Viewer -- interne Dokumente
verlassen das Netz nicht). Excel-Zellen werden bewusst per textContent
statt ueber SheetJS' eigene HTML-Ausgabe gesetzt, um unabhaengig von
deren Escaping-Verhalten sicher zu sein. Neue Route /fileshare/view mit
fester Endungs-Positivliste, nosniff + restriktiver CSP.
- Uploads bis 15MB erlaubt (MAX_CONTENT_LENGTH in app.py) -- dafuer musste
auch nginx' client_max_body_size (Standard 1MB) auf 16MB angehoben werden,
sowohl im Vorlagen-Code (_NGINX_PROXY_LOCATIONS/_render_nginx_config,
fuer die naechste Anwendung ueber Einstellungen->NGINX) als auch in der
Fresh-Install-Vorlage. Neuer 413-Errorhandler zeigt eine Flash-Meldung
statt Werkzeugs nackter Fehlerseite.
- install.sh: known_hosts (liegt unter /srv/tesm) von rsync --delete
ausgenommen -- fehlte bisher in der Exclude-Liste (anders als sqlite.db/
fernet.key/secret.key) und waere bei jedem Update verlorengegangen.
Live auf POETEST getestet: Baum-Vorbefuellung, Lazy-Load von
Geschwisterordnern, alle 5 Vorschautypen (PDF-Route direkt verifiziert,
Bild/Text/Word/Excel zusaetzlich visuell), Abschneide-Erkennung sowohl bei
kleiner als auch bei >512KB-Datei.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Neue ldap_fileshare_mappings-Tabelle (AD-Gruppe -> Freigabe-UNC+Label),
verwaltet in Einstellungen -> LDAP wie die bestehende App-Gruppenzuordnung
(inkl. AD-Gruppen-Dropdown per "Gruppen laden", UNC-Backslash/Slash-
Normalisierung).
- Login-Gate: optionale "Erforderliche AD-Gruppe fuer Login" (Dropdown,
gleiches Lade-Muster) -- ohne Mitgliedschaft scheitert der Login wie bei
falschen Zugangsdaten, ohne Hinweis auf den eigentlichen Grund.
- Sessionabhaengiges Mounten: beim LDAP-Login werden alle per AD-Gruppe
zugeordneten Freigaben mit den eigenen (nie gespeicherten) Zugangsdaten
per mount.cifs unter /mnt/tesm-shares/<session-token>/ gemountet, beim
Logout wieder ausgehaengt. Passwort läuft ausschliesslich ueber die
PASSWD-Umgebungsvariable von mount.cifs. Aufraeum-Sweep (30 Min-Takt,
12h-Schwelle) und Cleanup-on-Startup als Sicherheitsnetz.
- Zwei unabhaengige Gates: TESM-Rechtesystem (fileshare.view/.create/.edit,
neue PERMISSIONS-Gruppe fileshare_group) UND AD-Gruppenmitgliedschaft
muessen beide zutreffen, sonst wird nicht gemountet und der Menuepunkt
"Dateifreigaben" bleibt verborgen.
- Neuer Dateibrowser (/fileshare, .../download, .../upload, .../mkdir,
.../delete, .../rename) mit Breadcrumbs, Upload/Download, Anlegen/
Umbenennen/Loeschen -- Pfadaufloesung ausschliesslich ueber
os.path.realpath-Vergleich gegen die eigene Mount-Root (Path-Traversal-
Schutz), zusaetzlich secure_filename() fuer Datei-/Ordnernamen.
- KRITISCHER Bugfix waehrend Live-Test gefunden: generate_ips.py importiert
app.py als Hilfsmodul (u.a. aus poe.sh/tesm-check.service heraus, alle
paar Sekunden) -- das fuehrte Modul-Level-Code erneut aus und hat aktive
Fileshare-Mounts anderer Sitzungen sofort wieder ausgehaengt. Neues
TESM_WEB_PROCESS-Env-Flag (nur in tesm.service gesetzt) schuetzt
Cleanup-on-Startup und den Sweep-Thread davor, bei einem reinen
Modul-Import erneut zu laufen.
- install.sh: cifs-utils ergaenzt (liefert mount.cifs).
- SCHEMA_VERSION 1 -> 2, VERSION 1.0.10 -> 1.1.0.
Live auf Testbox (192.168.82.51) und POETEST (192.168.80.202) getestet:
Login-Gate (erlauben/verbieten/reset), Rechte-Gate (Mount+Menuepunkt nur
mit fileshare.view), vollstaendiger Datei-Workflow (Upload/Download/
Umbenennen/Anlegen/Loeschen) gegen eine echte SMB-Freigabe, Path-Traversal
gegen alle 5 Routen (serverseitig verifiziert, kein Escape), sauberes
Unmount bei /logout.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Umbenennung: Einstellungen -> SSL/TLS heisst jetzt Einstellungen -> NGINX
(Route /settings/ssl -> /settings/nginx, Recht settings_ssl.* ->
settings_nginx.*, Icon "server" statt "shield") -- die Seite verwaltet
den kompletten Reverse-Proxy, nicht mehr nur SSL.
Neu:
- Domain (server_name) und HTTP-/HTTPS-Port sind jetzt einstellbar, nicht
mehr hart "_" bzw. 80/443. Aendert man den HTTP-Port weg von 80, wird
automatisch zusaetzlich ein minimaler Nur-ACME-Block auf Port 80
mitgeschrieben, da Let's Encrypt (HTTP-01) protokollbedingt immer Port 80
braucht, unabhaengig vom konfigurierten HTTP-Port.
- "RAW-Konfiguration"-Button zeigt die komplette aktuelle nginx-Config im
selben Popup-Stil wie das Live-Log (Wiederverwendung von
window.openRawLogModal, kein neuer Code dafuer noetig).
- Let's Encrypt: Zertifikat per certbot anfordern (certonly --webroot,
bewusst NICHT das certbot-nginx-Plugin -- das wuerde die App-eigene,
komplett selbstgeschriebene Config direkt editieren und bei der
naechsten Aenderung ueber diese Seite wieder verlieren). Automatische
Verlaengerung laeuft ueber certbots eigenen systemd-Timer/Cron
(Standard-Paketverhalten), ein Deploy-Hook
(/etc/letsencrypt/renewal-hooks/deploy/tesm-reload-nginx.sh, wird wie
die logrotate-Config bei jedem App-Start neu geschrieben) laedt nginx
nach jeder Verlaengerung neu. Fehlgeschlagene Anforderung (kein
oeffentlicher DNS-Eintrag o.ae.) wird sauber abgefangen und als
Fehlermeldung angezeigt, ohne etwas zu veraendern -- live mit einer
ungueltigen Test-Domain verifiziert.
- Zertifikatsquelle (Upload vs. Let's Encrypt) wird als eigenes Setting
(ssl_source) verfolgt, das Anwenden/Anzeigen/Entfernen beruecksichtigt
beide Quellen jeweils passend (Entfernen einer LE-verwalteten
Installation laeuft ueber "certbot delete", nicht ueber rohes
Datei-Loeschen, damit certbots eigene Renewal-Konfiguration nicht
verwaist).
- certbot zu install.sh's Paketliste hinzugefuegt.
Flask/gunicorn bindet jetzt an 127.0.0.1 statt 0.0.0.0 (tesm.service) --
direkter Zugriff auf Port 5000 aus dem Netz ist damit nicht mehr moeglich,
nur noch ueber den nginx-Reverse-Proxy (der ohnehin nur ueber 127.0.0.1
zugreift). Beantwortet die Nachfrage, ob Flask unerreichbar gemacht werden
kann.
Live auf Testsystem-Update-Restart verifiziert: Domain/Port-Aenderung
(HTTP-Port 8080 inkl. automatischem Zusatz-Block auf Port 80),
RAW-Config-Anzeige, Zertifikat-Upload, Let's-Encrypt-Fehlschlag sauber
abgefangen, Rueckbau auf Standardwerte (_, 80, 443, kein SSL) ueber
denselben Anwenden/Bestaetigen-Sicherheitsnetz-Pfad wie zuvor.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
SSL/TLS (neue Seite Einstellungen -> SSL/TLS, eigenes Recht settings_ssl.view/.edit):
- Zertifikat+privater Schluessel per Upload hinterlegbar (PEM), Validierung
VOR dem Speichern: gueltiges Zertifikat, noch nicht abgelaufen, Schluessel
passt zum Zertifikat (Abgleich ueber die oeffentlichen Schluessel als
DER-Bytes -- funktioniert algorithmus-unabhaengig fuer RSA/EC/...).
Speicherort /etc/tesm/ssl (bewusst ausserhalb von /srv/tesm, damit
rsync --delete beim Update das Zertifikat nicht mitloescht), Key mit
chmod 600.
- HTTPS aktivieren schreibt/validiert (nginx -t) eine neue nginx-Konfiguration
und laedt erst danach neu; Port 80 leitet dann automatisch auf https weiter.
HSTS optional zuschaltbar, setzt aktives SSL voraus.
- Gleiches Sicherheitsnetz wie bei den Netzwerkeinstellungen: nach dem
Anwenden 45s Zeitfenster zum Bestaetigen, sonst automatischer Rueckroll
der nginx-Konfiguration UND der ssl_enabled/hsts_enabled-Einstellungen.
Beide Pfade (Timeout-Rollback und Bestaetigen-verhindert-Rollback) live
auf Testsystem-Update-Restart verifiziert, inklusive eines ECHTEN,
ungeplant ausgeloesten Rollbacks waehrend des Testens.
- Noch KEINE Let's-Encrypt-Automatisierung (siehe Kommentar im Code) --
alle bekannten Instanzen laufen intern ohne oeffentliche Domain, HTTP-01
wuerde dort nicht funktionieren; kann bei Bedarf separat ergaenzt werden.
install.sh: Update-Idempotenz ("nur anfassen, was sich geaendert hat"):
- install_if_changed()-Helfer: Custom-Skript/systemd-Units/MOTD-Banner
werden nur noch bei tatsaechlich geaendertem Inhalt neu geschrieben,
daemon-reload nur wenn sich mindestens eine Unit geaendert hat.
- nginx-Konfiguration wird nur noch bei einer Frischinstallation aus dem
Repo-Template geschrieben -- eine bereits vorhandene (egal ob unveraendert
oder von der App selbst um SSL erweitert) wird nie wieder ueberschrieben.
Voraussetzung dafuer, dass die neue SSL-Funktion ein Update ueberlebt.
update.sh: komplette Sonderbehandlung fuer die zwei TESM vorausgehenden
Generationen (rpi-check.service, poe_web.service/poe_manager) entfernt --
auf allen aktuellen Installationen laengst migriert. Deaktivierte
Unit-Dateien dieser Generationen wurden auf Testsystem-Update-Restart
zusaetzlich manuell endgueltig geloescht (waren dort seit einer Migration
im September 2025 nur deaktiviert, nicht entfernt).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Root Cause (live auf POETEST reproduziert): /var/log/tesm war 0o777
(noch aus einer Zeit vor dem Umstieg auf root-laufende systemd-Units).
logrotate verweigert seit den aktuellen Haertungen jede Rotation in
einem group-/world-writable Verzeichnis ("insecure permissions") komplett
-- live.log wuchs auf POETEST seit der letzten Neuinstallation (9 Tage)
unbegrenzt, /var/lib/logrotate/status hatte fuer tesm ueberhaupt keinen
Eintrag. Direkte Folge: die neue Verlauf-Seite blieb leer, weil nie
live.log.1/.2/... entstanden.
Fix:
- os.chmod(TESM_LOG_DIR) und os.chmod(TESM_CLIENT_UPDATE_LOG_DIR) in
app.py: 0o777 -> 0o755 (tesm.service/tesm-check.service laufen beide
als root, kein anderer Dienstbenutzer schreibt hierher -- Keas eigenes
Log liegt separat unter /var/log/kea/). Laeuft bei jedem App-Start,
heilt bestehende Installationen also automatisch beim naechsten
Neustart/Update.
- install.sh: chmod 777 -> chmod 755 fuer /var/log/tesm bei einer
Frischinstallation, veraltete/falsche Kommentar-Begruendung (angeblich
auch fuer Kea noetig) korrigiert.
- _write_logrotate_config(): zusaetzlich 'su root root' in der
generierten Konfiguration als Absicherung, falls das Verzeichnis aus
irgendeinem Grund wieder zu weit geoeffnet wird.
- VERSION 1.0.5 -> 1.0.6.
Live verifiziert auf Testsystem-Update-Restart: logrotate -d zeigt nach
dem Fix keine 'insecure permissions'-Fehler mehr, ein erzwungener Lauf
(logrotate -f) erzeugt sauber live.log.1/live.log.2.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Live gefunden: der 'Schema unveraendert -> kein Backup'-Kurzschluss aus
v1.0.3 liess einen rein CODE-seitigen Fehler im neuen Paket (z.B. ein
kaputtes app.py -- mit dem DB-Schema hat das nichts zu tun) komplett
ungeschuetzt durch. Backup + Health-Check + ggf. Rollback laufen jetzt
bei JEDEM Update einer bestehenden Installation, unabhaengig von
SCHEMA_VERSION -- die bleibt nur noch fuer den Wortlaut der Log-Meldung
relevant (In-Place-Update vs. Migration).
Zusaetzlich beim Rollback selbst: 'mv' durch 'cp -a' beim Zurueckspielen
ersetzt, plus die fehlgeschlagene Installation vorher separat beiseite
gelegt (/srv/tesm-failed-update-<timestamp>) statt geloescht -- vorher
wurde das Backup beim Zurueckspielen selbst zum neuen /srv/tesm und
verschwand dadurch als eigenstaendige Sicherung (widersprach der
eigenen Zusage 'Backup bleibt erhalten').
Live auf der Testbox in allen drei Pfaden verifiziert:
- Erfolgreiches Update: Backup erstellt, Health-Check bestanden,
Backup geloescht, Daten erhalten.
- Migration (kuenstlich abweichende SCHEMA_VERSION, echte Struktur
unveraendert): Backup erstellt, Health-Check bestanden, Backup
geloescht.
- Fehlgeschlagenes Update (absichtlich kaputte app.py): Backup
erstellt, Health-Check schlaegt korrekt fehl, automatischer Rollback
stellt den letzten funktionierenden Zustand wieder her (Dienst aktiv,
Health-Check 200, Datenbank-Canary erhalten), Backup UND
fehlgeschlagene Installation bleiben als zwei getrennte Verzeichnisse
zur Fehlersuche erhalten.
VERSION: 1.0.4 (v1.0.3 hatte die oben beschriebene Luecke und wurde
bereits ausgerollt, daher hier ein neues Release statt eines erneuten
Tags auf v1.0.3).
install.sh erkennt jetzt selbststaendig eine bestehende Installation
(an sqlite.db) und verhaelt sich entsprechend:
- Keine vorhandene Installation -> normale Frischinstallation (wie
bisher).
- Vorhanden, SCHEMA_VERSION unveraendert -> einfaches In-Place-Update,
kein Backup noetig (rsync schliesst sqlite.db ohnehin aus).
- Vorhanden, SCHEMA_VERSION unbekannt/abweichend -> Update MIT
Sicherheitsnetz: die KOMPLETTE bisherige Installation (Code, venv,
Datenbank, Schluessel) wird vorher 1:1 nach
/srv/tesm-backup-pre-migration-<timestamp> kopiert (bewusst nicht nur
die Datenbank -- ein reines DB-Rollback wuerde beim naechsten Start
mit dem NEUEN Code sofort denselben Migrationsfehler wiederholen).
Die eigentliche Schema-Migration uebernimmt danach die App selbst
beim Start (_ensure_schema() in app.py). Kommt der Dienst
anschliessend per HTTP-Check auf /login nachweislich NICHT gesund
hoch, wird automatisch die komplette Sicherung zurueckgespielt und
bleibt zur manuellen Pruefung erhalten. Kommt er gesund hoch, gilt
die Migration als erfolgreich und das Backup wird geloescht.
update.sh delegiert diese gesamte /srv/tesm-spezifische Logik jetzt an
install.sh und ist selbst nur noch fuer das Ablösen AELTERER, TESM
vorausgehender Installationsstufen zustaendig (PoE Manager unter
/srv/poe_manager, das urspruengliche reine Bash-Setup) -- bleibt aber
als bequemer Ein-Skript-Bootstrapper (Download + Entpacken + Aufruf von
install.sh) bestehen, wie gewuenscht. install.sh selbst ist dadurch
jetzt auch OHNE update.sh direkt aus einem entpackten Release-Paket
lauffaehig und erkennt ein Update trotzdem korrekt.
VERSION: 1.0.3 (SCHEMA_VERSION unveraendert, 1 -- keine echte Schema-
Aenderung in diesem Release, In-Place-Update-Pfad bleibt der Normalfall).
tesm.service startete die App bisher direkt per 'python3 app.py', was
Flasks eingebauten Werkzeug-Entwicklungsserver nutzt -- der meldet bei
jedem Start 'WARNING: This is a development server. Do not use it in a
production deployment.' und ist bewusst nicht für Dauerbetrieb gebaut.
Umgestellt auf Gunicorn, mit zwei bewusst gesetzten Einschraenkungen:
- '--workers 1': die App haelt laufende Wartungs-Jobs/Import-Vorschauen
nur im Arbeitsspeicher DES EINEN Prozesses (_maintenance_jobs,
_pending_imports, ...) -- mehrere Worker-Prozesse wuerden diesen
Zustand auf mehrere, sich nicht kennende Prozesse verteilen.
- '--worker-class gthread --threads 16' statt eines async-Workers: das
Browser-SSH-Terminal (flask-sock) belegt pro offener WebSocket-
Verbindung einen blockierenden Thread.
- '--timeout 0': deaktiviert Gunicorns eigenen 30s-Worker-Timeout, der
sonst auch eine offene, aber gerade inaktive SSH-Terminal-Verbindung
killen wuerde.
Live auf POETEST verifiziert: keine WARNING-Zeile mehr, genau 1 Master-
+ 1 Worker-Prozess, alle Kernseiten weiterhin 200, und als kritischer
Regressionstest das WebSocket-SSH-Terminal Ende-zu-Ende gegen einen
echten Host erfolgreich getestet (Host-Key/Passwort-Prompt/Login/Shell).
Dabei einen unabhaengigen Altlast-Fehler in POETESTs venv gefunden und
allgemein in install.sh gefixt: './venv/bin/pip' hat eine zur
Venv-Erstellung fest einprogrammierte Shebang-Zeile -- nach der
historischen Umbenennung 'poe_manager' -> 'tesm' zeigte sie auf einen
nicht mehr existierenden Pfad und schlug mit 'cannot execute: required
file not found' fehl. 'python3 -m pip' umgeht das, da nur der (korrekt
referenzierte) venv-Python-Interpreter selbst gebraucht wird -- betrifft
sonst auch jedes kuenftige In-Place-Update auf einem aehnlich
gewachsenen Host.
"clear" schlägt ohne TERM/Pseudo-Terminal mit Exit-Code 1 fehl ("TERM
environment variable not set") -- z.B. genau dann, wenn install.sh (wie von
update.sh vorgesehen) per SSH exec_command ohne pty aufgerufen wird. Mit
"set -e" hat das die GESAMTE Installation sofort an Zeile 2 abgebrochen,
noch bevor auch nur ein apt-Paket installiert oder eine einzige Zeile ins
Install-Log geschrieben wurde -- kein Fehlertext, kein Hinweis, einfach
Stille. Live reproduziert: update.sh lud das Release-Paket korrekt herunter
und entpackte es, install.sh selbst tat aber schlicht nichts.
Behoben: "clear" ist jetzt bewusst nie fatal (2>/dev/null || true) -- rein
kosmetisch, hat keinen Einfluss auf die eigentliche Installation.
- Login-Seite: WiS-/TESM-Logo und "Melde dich an, um fortzufahren" aus der
Box entfernt; die TESM-Wortmarke mit Funktionszeile ist jetzt der
tatsächliche Seitenhintergrund (absolut positioniert, hinter allem), die
Box liegt zentriert davor. Nebenbei zwei echte Bugs gefunden und behoben:
fehlendes Cache-Busting auf login.html (asset_url() wie in base.html,
sonst hält nginx' 7-Tage-Cache eine alte style.css/app.js fest) und ein
durch den fehlenden align-items-Wert auf die volle Höhe gestrecktes
Login-Formular.
- Neuer SSH-Login-Banner (dynamisches MOTD) mit Live-Flottenstatus statt der
Standard-Ubuntu-Willkommensmeldung: srv/tesm/motd_banner.py liest
Online/Offline- und DHCP-Status aus denselben Quellen wie das
Web-Dashboard (get_device_status()/_dhcp_read_leases()), eingebunden über
etc/update-motd.d/50-tesm. install.sh deaktiviert dabei die Standard-
Ubuntu-MOTD-Skripte (chmod -x, reversibel) und installiert den neuen Hook.
Logging-Infrastruktur (Logs können nach einem Import mit vielen Geräten
schnell vollaufen):
- Vier eigene, rotierte Log-Dateien statt allem in /var/log vermischt:
Live (poe.sh, jetzt eine einzige durchgehend fortgeschriebene Datei
statt vorher ein neues rpi-<timestamp>.log pro Dienst-Neustart),
Änderungen (Spiegel der audit_log-Tabelle), App (Flask-Fehler/
Warnungen, vorher nur im journalctl-Output), Kea-DHCP (eigene
Logging-Konfiguration in der generierten kea-dhcp4.conf)
- Kea-DHCP-Log bewusst NICHT im gemeinsamen Verzeichnis: Keas
AppArmor-Profil erlaubt Schreibzugriff exklusiv auf
/var/log/kea/kea-dhcp4.log (kein Verzeichnis-Wildcard) -- live als
"DENIED ... mknod ... .lock" reproduziert; die App nutzt Keas eigenen,
bereits erlaubten Pfad statt das AppArmor-Profil eines Systemdienstes
aufzuweichen. logrotate deckt trotzdem beide Verzeichnisse mit einer
Regel ab
- Neue Log-Seite "Kea-DHCP" (Logs-Bereich, eigenes Recht logs_kea.view)
- Rotation (Intervall täglich/wöchentlich/monatlich, Aufbewahrung als
Anzahl Rotationen) unter Systemeinstellungen → Logs einstellbar,
generiert /etc/logrotate.d/poe-manager; copytruncate, da keiner der
drei schreibenden Prozesse ein Reopen-Signal implementiert
- Einmalige Migration räumt alte, verwaiste rpi-*.log-Dateien auf
Zeitzone (Systemeinstellungen, mit Hostname in einer Kachel "Host"):
setzt die System-Zeitzone per timedatectl, damit Logs/Änderungsverlauf
die tatsächliche lokale Zeit statt UTC zeigen. time.tzset() direkt nach
dem Setzen lässt die bereits laufende poe_web.service-Instanz die neue
Zeitzone sofort übernehmen, ohne Neustart.
Systemeinstellungen neu sortiert: Host + Netzwerkeinstellungen (beide
Host-Konfiguration) nebeneinander, dann Logs, dann Prüfintervall.
Netzwerkeinstellungen: DNS-Anzeige zeigte bisher den lokalen
systemd-resolved-Stub (127.0.0.53) aus /etc/resolv.conf statt der
echten Server -- jetzt über "resolvectl dns <interface>", inkl. Fix
für eine falsch geparste IPv6-Fortsetzungszeile ohne Label, und
gefiltert auf IPv4 (diese Seite verwaltet nur IPv4-Konfiguration, per
IPv6-Router-Advertisement gelernte Server sind hier irreführend).
Dashboard: die vier Stat-Kacheln (Online/Offline/Deaktiviert/Gesamt)
sind jetzt klickbare Filter (blenden alle anderen Abschnitte aus, Klick
auf die aktive Kachel oder "Gesamt" hebt den Filter auf); jeder
Abschnitt lässt sich unabhängig davon per Klick auf seinen Titel ein-/
ausklappen. Beide Zustände bleiben über die automatischen Live-Updates
hinweg erhalten.
Design-Politur:
- Hauptinhaltsbereich nutzt jetzt die volle verfügbare Breite (kein
Bootstrap-typisches festes max-width mehr) -- Modals bleiben bewusst
bei ihrer kompakten, festen Breite
Akzentfarbe exakt auf das Orange aus dem WiS-Logo abgestimmt
(#ff7100, per Pixel-Sampling ermittelt) statt einer nur ähnlichen,
frei gewählten Nuance
- Abmelden führt zurück auf das (auch ohne Login einsehbare) Dashboard
statt auf die Login-Seite
Live gegen die echte Test-VM verifiziert: alle vier Logs werden
geschrieben, logrotate-Konfiguration korrekt generiert, ein
Zeitzonenwechsel (UTC → Europe/Berlin) wirkte sofort, DNS-Anzeige zeigt
jetzt die echten IPv4-Server (1.1.1.1, 8.8.8.8) statt Stub/IPv6,
Dashboard-Filter/-Collapse und Logout-Redirect per Playwright bestätigt.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Auf der Test-VM war 'ping' nicht vorhanden (Minimal-Ubuntu-Image liefert
es nicht immer vor) -- fuer die Erreichbarkeitspruefung der Geraete
(Pruefintervall / rpi-check-Dienst) zwingend noetig. Live auf dem
Testsystem nachinstalliert und verifiziert.
Live auf frischem Testsystem (Ubuntu 24.04, 192.168.80.114) verifiziert:
Skript brach beim Kopieren nach /srv/poe_manager mit 'rsync: command
not found' ab, weil rsync nirgends in der apt-get-install-Zeile stand.
- Neues, eigenständiges Frontend (Sidebar, zentriertes Logo in der Topbar,
Dark/Light-Theme, Karten-Dashboard, Toasts/Modals statt Bootstrap)
- Oeffentliches Kurz-Dashboard ohne Login (Status-Uebersicht)
- Browser-SSH-Terminal (paramiko, plattformunabhaengig) zum Testen von
Switch-Zugangsdaten inkl. interaktiver Host-Key-Bestaetigung
- Granulares Rechtesystem mit Gruppen (Devices/Switches-Berechtigungen)
- Aufgeraeumtes Backend mit konfigurierbaren Pfaden, auto-generierten
Secrets statt hart codierter Werte im Original
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>