diff --git a/docs/GNAI-006-SEC_VPN.md b/docs/GNAI-006-SEC_VPN.md index 5460e11..fa24f8e 100644 --- a/docs/GNAI-006-SEC_VPN.md +++ b/docs/GNAI-006-SEC_VPN.md @@ -4,7 +4,7 @@ **Titel:** Homelab / Infrastructure – WireGuard VPN Gateway (LXC) Setup & Access Control -**Version:** 1.1 +**Version:** 1.2 **Status:** In Umsetzung (Schritt 1–4 & 7 fertig, Step 5/6/9 offen) @@ -50,7 +50,13 @@ Dieses Dokument beschreibt die verbindliche Einrichtung, Härtung und Verifikati * **AllowedIPs Client-Seitig:** `10.0.0.90/32` * **Erlaubte Ziele:** Exklusiv LLM-Server (`10.0.0.90`, z. B. SSH Port 22) -> **Hinweis wg-easy / AllowedIPs (Umsetzung 2026-09-09):** Die **serverseitige** `AllowedIPs`-Eintragung wird von wg-easy beim Konfig-Generate hartkodiert als `client.address/32` (`/app/lib/WireGuard.js`, `__saveConfig()`). Es existiert **kein API-Endpoint**, um die serverseitigen AllowedIPs abweichend zu setzen. Für die gewünschte Architektur ist dies korrekt: Serverseitig darf ein Peer nur aus seiner eigenen VPN-IP kommen; das **Routing (Split-Tunneling) wird clientseitig** über die AllowedIPs im heruntergeladenen Profil bestimmt (FullAdmin: `10.0.0.0/24, 10.8.0.0/24`; LLMAdmin: `10.0.0.90/32, 10.8.0.0/24`). **Offen:** Verifikation, dass die exportierten Client-Profile diese clientseitigen AllowedIPs enthalten (wg-easy bietet hierzu je nach Version nur eingeschränkte Konfiguration – siehe Implementation-Log, Punkt „allowedIPs-Limitation“). +> **Hinweis wg-easy / AllowedIPs (Umsetzung 2026-09-09):** Die **serverseitige** `AllowedIPs`-Eintragung wird von wg-easy hartkodiert als `client.address/32` (`/app/lib/WireGuard.js`, `__saveConfig()`) – korrekt für diese Architektur (Peer kommt nur aus seiner eigenen VPN-IP). Das **clientseitige Routing** (Split-Tunneling) wird in den heruntergeladenen Profilen aus der Env-Variable **`WG_ALLOWED_IPS`** generiert (`/app/lib/WireGuard.js`, `getClientConfiguration()`). Default von wg-easy wäre `0.0.0.0/0, ::/0` (Full-Tunnel!) – **umgestellt auf Split-Tunnel** via `/opt/wg-easy/environment`: +> +> ``` +> WG_ALLOWED_IPS=10.0.0.0/24,10.8.0.0/24,10.0.0.2/32,10.0.0.10/32,10.0.0.90/32 +> ``` +> +> ⚠️ **Android-Limitierung:** WireGuard für Android akzeptiert in `AllowedIPs` nur `/32`-Einträge (keine `/24`-CIDRs). Die zusätzlichen `/32`-Einträge (Fritzbox, Proxmox, NPM, LLM-Server) existieren daher **ausschließlich** für Android-LLMAdmin; Laptop-FullAdmin nutzt die `/24`-Netzwerke. Konsequenz: Neue LAN-Hosts müssen **beide** Einträge pflegen (CIDR + `/32`) – kein per-Client-Override in wg-easy möglich (eine Env-Variable für alle Profile). --- @@ -201,15 +207,17 @@ Peers über die wg-easy-API erstellt (UI unter `http://10.0.0.15:51821`, Login ` Server-Config `wg0.conf` enthält beide Peers mit `PresharedKey`; serverseitig `AllowedIPs = 10.8.0.2/32` bzw. `10.8.0.3/32` (wg-easy-Hardcode, siehe Hinweis in Sektion 2). -Verifikation ausstehend: ⏳ heruntergeladene Client-Profile auf clientseitige `AllowedIPs`, `Endpoint vpn.ghostnet.selfhost.eu:51820` und `PersistentKeepalive = 25` kontrollieren (abhängig von Step 5/6). +Verifikation ausstehend: ⏳ heruntergeladene Client-Profile auf clientseitige `AllowedIPs` (aus `WG_ALLOWED_IPS`) und `Endpoint vpn.ghostnet.selfhost.eu:51820` kontrollieren (abhängig von Step 5/6). Hinweis: `WG_PERSISTENT_KEEPALIVE` ist nicht gesetzt (Default `0`) – bei NAT/Router-Wechsel-Problemen auf `25` setzen. -### 8. Split-Tunneling-Routing verifizieren ⏳ +### 8. Split-Tunneling-Routing verifizieren ⏳ (Konfig gesetzt, End-to-End offen) -Abhängig von Step 7. Falls die exportierten wg-easy-Profile die clientseitigen AllowedIPs nicht wie gewünscht enthalten, sind Alternativen zu evaluieren: +✅ **Erledigt (2026-09-09):** `WG_ALLOWED_IPS` in `/opt/wg-easy/environment` gesetzt (Split-Tunnel statt wg-easy-Default Full-Tunnel), `wg-easy.service` neu gestartet, Env im Container verifiziert. -* wg-easy-Einstellungen/Version prüfen (neuere Versionen unterstützen clientseitige `AllowedIPs`-Overrides, z. B. über `WG_ALLOWED_IPS`-Template oder Peer-Settings) -* Manuelle Client-Configs als Fallback (QR/Datei direkt aus `wg0.conf`-Daten + clientseitiger Keys) -* Direct-Edit von `/opt/wg-easy/data/wg0.json` nur für serverseitige Exceptions (hier **nicht** erforderlich) +⏳ **Offen (abhängig von Step 5/6 + Endgerät):** + +* Heruntergeladenes Profil Laptop-FullAdmin: `AllowedIPs = 10.0.0.0/24,10.8.0.0/24,…` + `Endpoint vpn.ghostnet.selfhost.eu:51820` prüfen, LAN-Hosts durch VPN erreichbar, Internet-Traffic bleibt **außerhalb** des Tunnels +* Android-LLMAdmin: Routing nur auf `10.0.0.90` (LLM-Server) + VPN-Subnetz; andere LAN-Hosts nicht über VPN erreichbar +* DNS `10.0.0.1` via `WG_DEFAULT_DNS` (nur innerhalb Tunnelsolutions relevant – prüfen, ob Android DNS-Einstellung akzeptiert) ### 9. Penetrationstest & Ingress-Verifikation über Mobilfunk ⏳ @@ -247,8 +255,8 @@ Deaktiviere WLAN auf beiden Testgeräten (reiner 4G/5G-Pfad), VPN aktivieren: | 4 | wg-easy Quadlet + wg0 | ✅ | | 5 | Fritzbox Portforward | ⏳ User | | 6 | NPM-Subdomain `vpn.ghostnet.selfhost.eu` | ⏳ User | -| 7 | Peers Laptop-FullAdmin / Android-LLMAdmin | ✅ (Client-Config-Verifikation offen) | -| 8 | Split-Tunnel-Routing verifizieren | ⏳ | +| 7 | Peers Laptop-FullAdmin / Android-LLMAdmin | ✅ | +| 8 | Split-Tunnel-Konfig (`WG_ALLOWED_IPS`) | ✅ (End-to-End-Verifikation ⏳ mit Step 9) | | 9 | Penetrationstest Mobilfunk | ⏳ | Details & Entscheidungen: siehe `GNAI-006-SEC_VPN_Implementation-Log.md`. @@ -261,3 +269,4 @@ Details & Entscheidungen: siehe `GNAI-006-SEC_VPN_Implementation-Log.md`. | --- | --- | --- | | 1.0 | – | Erster Draft | | 1.1 | 2026-09-09 | Umsetzung dokumentiert: Hostname `wireguard` (nicht `wireguard-gateway`); Testmatrix auf „Ausstehend“ (keine Tests über Mobilfunk gelaufen); `PASSWORD_HASH` = bcrypt (wg-easy/bcryptjs), nicht Argon2; Quadlet mit `EnvironmentFile=`; nftables-Konfig & ip_forward dokumentiert; wg-easy-allowedIPs-Limitation vermerkt; Umsetzungszustand-Tabellen & Implementation-Log ergänzt | +| 1.2 | 2026-09-09 | Split-Tunneling konkretisiert: clientseitige AllowedIPs kommen aus Env-Variable `WG_ALLOWED_IPS` (Default `0.0.0.0/0` = Full-Tunnel!); `WG_ALLOWED_IPS` in `/opt/wg-easy/environment` gesetzt, Container neu gestartet, Env im Container verifiziert; Android-/24-Limitierung dokumentiert (nur `/32`-Einträge); Schritt 8 in „Konfig gesetzt / End-to-End offen“ zerlegt; `WG_PERSISTENT_KEEPALIVE`-Hinweis ergänzt | diff --git a/docs/GNAI-006-SEC_VPN_Implementation-Log.md b/docs/GNAI-006-SEC_VPN_Implementation-Log.md index 8818cbd..223ab08 100644 --- a/docs/GNAI-006-SEC_VPN_Implementation-Log.md +++ b/docs/GNAI-006-SEC_VPN_Implementation-Log.md @@ -2,6 +2,7 @@ **Bezug:** GNAI-006-SEC_VPN.md **Session:** 2026-09-09 (Agent-Session, Arbeit per SSH auf LXC 105) +**Letzte Aktualisierung:** 2026-09-09 16:50 – `WG_ALLOWED_IPS`-Befund (Sektion 3.3), Split-Tunnel-Konfig auf LXC gesetzt, O3/O4 aktualisiert **Zweck:** Nachvollziehbare Dokumentation der Umsetzungsschritte, Abweichungen vom Draft, technischen Befunde und Fehlerbehebungen. --- @@ -87,13 +88,13 @@ Client-UUIDs (LXC): `Laptop-FullAdmin` = `2e9ed910-36dc-4fbd-8694-8d904670ea19`, * **Serverseitig:** `__saveConfig()` in `/app/lib/WireGuard.js` (Container) generiert pro Peer `AllowedIPs = ${client.address}/32` – hartkodiert. Das Feld `allowedIPs` im internen `wg0.json` existiert, wird vom Create-Endpoint aber **nie** befüllt (stets `None`). * **Konsequenz:** Es gibt keinen API-Path, um serverseitige AllowedIPs freier zu konfigurieren (z. B. ein Peer aus mehreren VPN-IPs). * **Architektonische Bewertung:** Für unser Modell ist das serverseitige `/32`-Verhalten **korrekt und gewünscht**: Ein Peer darf nur Traffic aus seiner eigenen VPN-IP senden. Das eigentliche Routing (Split-Tunneling) ist **clientseitig** bestimmt – die AllowedIPs im installierten Client-Profil entscheiden, welche Zielnetzwerke über den Tunnel laufen. -* **Offene Frage / nächste Verifikation:** ob die via wg-easy heruntergeladenen Client-Profile die gewünschten clientseitigen AllowedIPs enthalten: - * FullAdmin: `10.0.0.0/24, 10.8.0.0/24` - * LLMAdmin: `10.0.0.90/32` (ggf. `10.8.0.0/24` zusätzlich, damit Peer-seitiges Routing sauber bleibt) -* **Falls nicht:** Optionen (in dieser Reihenfolge): - 1. wg-easy-Einstellungen/Version prüfen – neuere Releases bieten Peer-spezifische `allowedIPs`-Overrides bzw. Template-Erweiterung (z. B. `WG_ALLOWED_IPS` / `allowedIPs` im JSON + Config-Regeneration) - 2. Client-Profile manuell aus den generierten `wg0.conf`-Daten + den clientseitigen Keys basteln (einmalige Aktion, dokumentierbar) - 3. Direct-Edit `/opt/wg-easy/data/wg0.json` + `wg-quick`-Reload – **nicht** für den hier relevanten Fall (serverseitig ist `/32` korrekt) +* **Clientseitig (gelöst 2026-09-09):** `getClientConfiguration()` in `/app/lib/WireGuard.js` generiert in den herunterladbaren Profilen `AllowedIPs = ${WG_ALLOWED_IPS}` – eine **globale** Env-Variable (kein per-Client-Override). Default: `0.0.0.0/0, ::/0` = **Full-Tunnel** (alle Traffic durch VPN) – nicht gewünscht. +* **Aktion:** `WG_ALLOWED_IPS` in `/opt/wg-easy/environment` ergänzt (Backup `environment.bak.`), `wg-easy.service` neu gestartet, Env im Container verifiziert: + ``` + WG_ALLOWED_IPS=10.0.0.0/24,10.8.0.0/24,10.0.0.2/32,10.0.0.10/32,10.0.0.90/32 + ``` +* **Android-Limitierung:** WireGuard für Android akzeptiert in `AllowedIPs` nur `/32` (keine `/24`-CIDRs). Die `/32`-Einträge (Fritzbox `.1`, Proxmox `.2`, NPM `.10`, LLM-Server `.90`) existieren daher explizit für Android-LLMAdmin; Laptop-FullAdmin nutzt die `/24`-Netzwerke. **Pflege-Konsequenz:** Neue LAN-Hosts müssen beide Varianten erhalten (CIDR + `/32`), danach `wg-easy.service` neu starten. +* **End-to-End-Verifikation (O4) bleibt offen:** heruntergeladene Profile prüfen (AllowedIPs + Endpoint), dann Routing-Tests auf den Endgeräten. --- @@ -118,8 +119,8 @@ Client-UUIDs (LXC): `Laptop-FullAdmin` = `2e9ed910-36dc-4fbd-8694-8d904670ea19`, | --- | --- | --- | --- | | O1 | Fritzbox 7530: Portforward **UDP 51820 → 10.0.0.15** | 👤 User | – | | O2 | NPM (10.0.0.10): Subdomain `vpn.ghostnet.selfhost.eu` anlegen | 👤 User | – | -| O3 | Client-Profile herunterladen (Laptop + Android), AllowedIPs/`Endpoint`/`PersistentKeepalive=25` prüfen | Agent + User | O1, O2 | -| O4 | Split-Tunnel-Routing end-zu-end verifizieren (Step 8 SOP) | Agent + User | O3 | +| O3 | Client-Profile herunterladen (Laptop + Android), `AllowedIPs` (aus `WG_ALLOWED_IPS`) + `Endpoint vpn.ghostnet.selfhost.eu:51820` prüfen | Agent + User | O1, O2 | +| O4 | Split-Tunnel-Routing end-zu-end verifizieren (Step 8 SOP; Konfig steht, s. 3.3) | Agent + User | O3 | | O5 | Penetrationstest Mobilfunk (Testmatrix Sektion 4 SOP) | gemeinsam | O1, O2, O3 | | O6 | Entscheidung: wg-easy-`PostUp`-iptables-Regeln via `WG_POST_UP`/`WG_PRE_DOWN` deaktivieren (hygiene) oder belassen (redundant, kein Konflikt) | Agent | O5 (nach Tests) | | O7 | Projekt-Kontext in `MEMORY.md` / Memory-Store persistieren | Agent | – |