Fix: nginx blockierte SSH-Terminal-WebSocket + DHCP-Stop bei Netzwerkaenderung

Auf der frisch installierten Test-VM zeigten sich zwei weitere reale Probleme:

1. "Verbindung testen" im Switch-Modal blieb dauerhaft auf "Getrennt"
   haengen, sobald ueber nginx (Port 80) statt direkt gegen Flask (Port
   5000) zugegriffen wurde. Ursache: etc/nginx/sites-available/poe_manager
   reichte den "Upgrade"/"Connection"-Header fuer /ws/ssh_terminal nicht
   durch -- Flask/Werkzeug lehnte den WebSocket-Handshake dadurch mit
   400 Bad Request ab, statt mit 101 Switching Protocols zu antworten.
   Live reproduziert und verifiziert (curl-Handshake direkt vs. durch
   nginx). Alle bisherigen Terminal-Tests dieser Session liefen ausschliesslich
   direkt gegen Flask, nie durch nginx, daher bis jetzt unentdeckt.
   Fix: eigener location /ws/ Block mit Upgrade-Map (Standard-nginx-Pattern)
   und grosszuegigen Timeouts fuer die lang offene Terminal-Verbindung.

2. Der Kea-DHCP-Server lief nach einer Aenderung der Host-Netzwerkkonfiguration
   unveraendert weiter, obwohl die konfigurierte Range nach einem
   IP-/Subnetzwechsel nicht mehr zum neuen Netz passen muss. Neue Funktion
   _stop_dhcp_service_if_active() stoppt Kea jetzt sowohl beim Anwenden
   als auch beim Auto-Rollback einer Netzwerkaenderung -- bewusst nur
   stoppen statt automatisch neu zu starten, die Range muss der Admin
   aktiv auf der DHCP-Seite pruefen und den Dienst dort bewusst wieder
   starten (gleiche Philosophie wie beim Rest der DHCP-Dienststeuerung).

Beide Fixes live auf der Test-VM verifiziert (WebSocket-Handshake per
curl, Netzwerk-Reapply ohne Fehler im journalctl-Log trotz nicht
installiertem Kea).
This commit is contained in:
2026-08-11 13:29:02 +02:00
parent 2b5d9d1b48
commit 3195df0fd3
3 changed files with 78 additions and 3 deletions
+38 -3
View File
@@ -1478,6 +1478,13 @@ def _revert_network_config(token):
except OSError:
pass
subprocess.run(["netplan", "apply"], timeout=20)
# Das Rollback ändert die Netzwerkkonfiguration erneut — falls
# zwischenzeitlich (z.B. manuell auf der DHCP-Seite) wieder gestartet,
# aus demselben Grund wie beim ursprünglichen Anwenden vorsorglich
# stoppen statt mit einer ggf. nicht mehr passenden Range weiterlaufen
# zu lassen.
if _stop_dhcp_service_if_active():
log_action_system("settings.network_revert", backup["interface"], "DHCP-Dienst wegen Rollback gestoppt")
log_action_system("settings.network_revert", backup["interface"], "automatisch nach Timeout zurückgerollt")
except Exception as e:
app.logger.error("Network auto-revert failed: %s", e)
@@ -1533,11 +1540,22 @@ def settings():
_pending_network_revert[token] = {"timer": timer, "backup": backup}
timer.start()
log_action("settings.network_apply", interface, f"Modus {mode}")
flash(
msg = (
f"Netzwerkkonfiguration angewendet. Falls diese Seite jetzt noch erreichbar ist, bitte "
f"unten bestätigen — sonst wird nach {NETWORK_REVERT_SECONDS}s automatisch zurückgerollt.",
"success",
f"unten bestätigen — sonst wird nach {NETWORK_REVERT_SECONDS}s automatisch zurückgerollt."
)
# Eine vorher passende DHCP-Range kann nach einer IP-/Subnetzänderung
# plötzlich zum falschen Netz gehören — der Dienst wird deshalb
# vorsorglich gestoppt statt (ggf. falsch) weiterzulaufen. Bewusst
# kein automatischer Neustart: die Range muss der Admin aktiv auf
# der DHCP-Seite prüfen/bestätigen, siehe Kommentar oben am Kea-Block.
if _stop_dhcp_service_if_active():
log_action("settings.network_apply", interface, "DHCP-Dienst wegen Netzwerkänderung gestoppt")
msg += (
" Der DHCP-Server wurde dabei gestoppt, da die konfigurierte Range zum neuen Netz nicht "
"mehr passen könnte — bitte auf der DHCP-Seite prüfen und bei Bedarf neu starten."
)
flash(msg, "success")
else:
flash(f"Anwenden fehlgeschlagen: {out}", "danger")
@@ -1694,6 +1712,23 @@ def _detect_dhcp_status():
return {"installed": installed, "path": kea_path, "active": active, "enabled": enabled, "version": version}
def _stop_dhcp_service_if_active():
"""Stoppt den Kea-DHCP-Dienst, falls er gerade läuft — aufgerufen, wenn
sich die Host-Netzwerkkonfiguration ändert (Systemeinstellungen >
Netzwerkeinstellungen). Eine vorher passende Range/Interface-Bindung
kann nach einer IP-/Subnetzänderung plötzlich zum falschen Netz gehören.
Bewusst nur stoppen, nicht automatisch neu starten — der Admin muss die
Range aktiv auf der DHCP-Seite prüfen/bestätigen, statt dass die App mit
einer möglicherweise falschen Konfiguration weiterläuft oder blind rät."""
try:
status = subprocess.run(["systemctl", "is-active", DHCP_SERVICE], capture_output=True, text=True, timeout=5)
if status.stdout.strip() != "active":
return False
return subprocess.run(["systemctl", "stop", DHCP_SERVICE], timeout=15).returncode == 0
except Exception:
return False
def _list_network_interfaces():
"""Echte Netzwerkschnittstellen dieses Hosts (ohne loopback) — Quelle
für die Interface-Auswahl, damit nur tatsächlich vorhandene Interfaces