From 0c14da34351992a6a6054ba979a45d0c3f31cd1b Mon Sep 17 00:00:00 2001 From: Tobias Leuschner Date: Sat, 9 May 2026 12:39:11 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20lokale=20Routing-Regeln=20f=C3=BCr=20Wi?= =?UTF-8?q?reGuard=20hinzugef=C3=BCgt=20und=20Versionsnummer=20auf=201.0.3?= =?UTF-8?q?1=20erh=C3=B6ht?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.py | 129 +++++++++++++++++++++++++++++----------------------- version.txt | 2 +- 2 files changed, 73 insertions(+), 58 deletions(-) diff --git a/app.py b/app.py index e67b8c0..af19a28 100644 --- a/app.py +++ b/app.py @@ -327,61 +327,32 @@ def wg_update_state(): error=None, has_config=has_conf) -import ipaddress as _ipaddress +# Helper-Script das wg-quick via PostUp/PreDown aufruft +_WG_ROUTE_SCRIPT = BASE_DIR / 'wg_route.py' +_WG_ROUTE_PY = '''\ +#!/usr/bin/env python3 +"""PiCopy: lokale IPs bei aktivem WireGuard-VPN erreichbar halten.""" +import sys, subprocess, ipaddress -_wg_managed_subnets: list = [] # beim Connect gemerkte Subnetze für sauberes Cleanup +def local_nets(): + r = subprocess.run(["ip", "-4", "addr", "show"], capture_output=True, text=True) + seen, res, iface = set(), [], "" + for ln in r.stdout.splitlines(): + if not ln[:1].isspace(): + iface = ln.split(":")[1].strip().split("@")[0] if ":" in ln else "" + elif iface and iface != "lo" and ln.strip().startswith("inet "): + try: + net = str(ipaddress.IPv4Interface(ln.split()[1]).network) + if net not in seen: seen.add(net); res.append(net) + except Exception: pass + return res - -def _local_subnets_before_vpn() -> list: - """Subnetze aller lokalen Interfaces (außer Loopback) via 'ip addr show'. - Muss VOR wg-quick up aufgerufen werden, damit wg-quick die Tabelle noch - nicht verändert hat.""" - r = subprocess.run(['ip', '-4', 'addr', 'show'], capture_output=True, text=True) - seen, result = set(), [] - current_iface = '' - for line in r.stdout.splitlines(): - if not line[:1].isspace(): - # Zeile wie "2: wlan0: str: + """Schreibt den Helper-Script und injiziert PostUp/PreDown in die Config, + falls 0.0.0.0/0 enthalten ist (Full-Tunnel). Gibt den ggf. geänderten + Config-Text zurück und schreibt ihn direkt in WG_CONF.""" + if '0.0.0.0/0' not in content: + return content + try: + _WG_ROUTE_SCRIPT.write_text(_WG_ROUTE_PY, encoding='utf-8') + _WG_ROUTE_SCRIPT.chmod(0o755) + except Exception as e: + log.warning(f'wg_route.py konnte nicht geschrieben werden: {e}') + return content + + post = f'PostUp = python3 {_WG_ROUTE_SCRIPT} up' + pred = f'PreDown = python3 {_WG_ROUTE_SCRIPT} down' + tag = str(_WG_ROUTE_SCRIPT) + + # Bereits vorhanden → nichts tun + if tag in content: + return content + + lines, new_lines, in_iface, done = content.splitlines(), [], False, False + for line in lines: + s = line.strip().lower() + # Alte PostUp/PreDown von uns entfernen (falls Pfad geändert) + if (s.startswith('postup') or s.startswith('predown')) and 'wg_route' in line: + continue + if line.strip() == '[Interface]': + in_iface = True + elif in_iface and line.strip().startswith('[') and not done: + new_lines += [post, pred, ''] + in_iface = False + done = True + new_lines.append(line) + if in_iface and not done: + new_lines += [post, pred] + + new_content = '\n'.join(new_lines) + try: + WG_CONF.write_text(new_content, encoding='utf-8') + WG_CONF.chmod(0o600) + except Exception as e: + log.warning(f'WG_CONF konnte nicht aktualisiert werden: {e}') + return new_content + + def wg_save_config(content: str): try: WG_CONF.parent.mkdir(parents=True, exist_ok=True) + content = _wg_inject_routing(content) WG_CONF.write_text(content, encoding='utf-8') WG_CONF.chmod(0o600) return True, '' diff --git a/version.txt b/version.txt index a7a8343..a8c6b78 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.0.30 \ No newline at end of file +1.0.31 \ No newline at end of file