Files
aza/AzA march 2026/aza_firewall.py

179 lines
5.2 KiB
Python
Raw Normal View History

2026-03-25 22:03:39 +01:00
# -*- coding: utf-8 -*-
"""
AZA Desktop Windows-Firewall-Handling.
Minimale lokale Regel fuer Backend auf 127.0.0.1:8000.
"""
import os
import sys
import subprocess
_RULE_NAME = "AZA Desktop - Lokale Kommunikation"
_BACKEND_PORT = 8000
def add_firewall_rule_if_needed() -> tuple[bool, str]:
"""
Legt eine minimale Windows-Firewall-Regel fuer das lokale AZA-Backend an.
Nur fuer aza_desktop.exe, TCP 8000, Remote 127.0.0.1 (localhost).
Gibt (erfolg, meldung) zurueck.
"""
if sys.platform != "win32":
return True, ""
exe_path = ""
if getattr(sys, "frozen", False):
exe_path = sys.executable
else:
# Dev: Basis-Pfad ermitteln
base = os.path.dirname(os.path.abspath(__file__))
for _ in range(5):
candidate = os.path.join(base, "aza_desktop.exe")
if os.path.isfile(candidate):
exe_path = candidate
break
parent = os.path.dirname(base)
if parent == base:
break
base = parent
if not exe_path or not os.path.isfile(exe_path):
return False, "aza_desktop.exe nicht gefunden"
exe_path = os.path.normpath(exe_path)
# Regel loeschen falls vorhanden (idempotent)
try:
subprocess.run(
[
"netsh", "advfirewall", "firewall", "delete", "rule",
f"name={_RULE_NAME}",
],
capture_output=True,
timeout=10,
creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, "CREATE_NO_WINDOW") else 0,
)
except Exception:
pass
# Regel anlegen
try:
result = subprocess.run(
[
"netsh", "advfirewall", "firewall", "add", "rule",
f"name={_RULE_NAME}",
"dir=in",
"action=allow",
f"program={exe_path}",
f"localport={_BACKEND_PORT}",
"protocol=tcp",
"remoteip=127.0.0.1",
],
capture_output=True,
text=True,
timeout=10,
creationflags=subprocess.CREATE_NO_WINDOW if hasattr(subprocess, "CREATE_NO_WINDOW") else 0,
)
if result.returncode == 0:
return True, "Firewall-Regel angelegt"
err = (result.stderr or result.stdout or "").strip()
return False, err or f"Rueckcode {result.returncode}"
except subprocess.TimeoutExpired:
return False, "Zeitueberschreitung"
except Exception as e:
return False, str(e)
def check_firewall_rule() -> dict:
"""
Prueft ohne Adminrechte, ob die erwartete Firewall-Regel existiert.
Gibt dict mit keys: exists, program_ok, port_ok, protocol_ok, remote_ok, detail
"""
result = {
"exists": False,
"program_ok": None,
"port_ok": None,
"protocol_ok": None,
"remote_ok": None,
"detail": "",
}
if sys.platform != "win32":
result["detail"] = "Nur unter Windows relevant"
return result
try:
proc = subprocess.run(
[
"netsh", "advfirewall", "firewall", "show", "rule",
f"name={_RULE_NAME}", "verbose",
],
capture_output=True,
text=True,
timeout=10,
creationflags=(
subprocess.CREATE_NO_WINDOW
if hasattr(subprocess, "CREATE_NO_WINDOW")
else 0
),
)
except Exception as e:
result["detail"] = f"Pruefung nicht moeglich: {e}"
return result
stdout = proc.stdout or ""
if proc.returncode != 0 or _RULE_NAME not in stdout:
result["detail"] = "Regel nicht vorhanden"
return result
result["exists"] = True
lower = stdout.lower()
if "tcp" in lower:
result["protocol_ok"] = True
else:
result["protocol_ok"] = False
if str(_BACKEND_PORT) in stdout:
result["port_ok"] = True
else:
result["port_ok"] = False
if "127.0.0.1" in stdout or "localsubnet" in lower:
result["remote_ok"] = True
else:
result["remote_ok"] = False
if "aza_desktop" in lower:
result["program_ok"] = True
else:
result["program_ok"] = False
problems = []
if not result["protocol_ok"]:
problems.append("Protokoll")
if not result["port_ok"]:
problems.append("Port")
if not result["remote_ok"]:
problems.append("Remote-IP")
if not result["program_ok"]:
problems.append("Programm")
if problems:
result["detail"] = "Regel vorhanden, Details abweichend: " + ", ".join(problems)
else:
result["detail"] = "Regel korrekt (TCP 8000, localhost, programmgebunden)"
return result
def get_firewall_hint_text() -> str:
"""Ruiger Hinweistext fuer den Nutzer bei Firewall-Popup oder Backend-Problem."""
return (
"Windows hat eine Rueckfrage zur lokalen Netzwerkkommunikation von AZA angezeigt.\n\n"
"AZA verwendet lokal laufende Komponenten auf diesem Computer. "
"Die Rueckfrage betrifft die interne Kommunikation der App.\n\n"
"Fuer den lokalen Betrieb von AZA koennen Sie fortfahren bzw. "
"den Zugriff fuer private Netzwerke erlauben."
)