Files
bf-infra/docs/warp-egress-native-crs326.md
Christoph Emsenhuber 528f133264 docs: Plan WARP-Egress (nativer Consumer-WARP als WG auf crs326-core)
Routing-Regel statt Mangle: Client-VLANs -> Routing-Tabelle warpout,
Router-Infra (wg-hub/cf-mesh/DNS/NTP) bleibt auf Liwest. Nur Plan, nichts ausgeführt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-25 19:24:13 +02:00

8.8 KiB

WARP-Egress — nativer Consumer-WARP (WireGuard) auf dem crs326-core

Status: PLAN (noch nicht ausgeführt) · 2026-09-25 · TP-Nummer bei Anlage vergeben (nächste freie, Stand TP46). Zweck: ausgehenden Client-Verkehr (alle VLANs) über Cloudflare egressen → geteilte CF-IP statt blockierter Heim-IP 90.146.33.177. Router-eigene Infra bleibt auf Liwest. Werkzeuge: MikroMCP (plan_changes→apply_plan) für den Router-Block · CF-MCP zum Verifizieren · wgcf nur für den einen Consumer-WARP-Key · sshpass nur für die WG-Zeilen, die MikroMCP nicht kann (TP16.1 §4.3). Muster: TP16.1 (docs/tp16.1-warp-mesh-native-crs326.md) — dort natives WG für Ingress, hier für Egress.


1. Kontext / Warum

  • Cannaleo-Origin 164.90.191.7 (DigitalOcean, multi-tenant, ~400 Vhosts) blockt die Heim-IP 90.146.33.177 hart (leerer 403, live 2026-09-25). Crawler ist aus, Projekt auf Eis — nur noch privates Surfen/Bestellen.
  • Ziel: CF-Egress für alle Clients/VLANs (nur die zwei aktiven Nutzer, keine Einschränkung nötig).

2. Entscheidung & Nicht-Ziele

  • Gewählt: nativer Consumer-WARP als zweites WG-Interface warpout auf crs326-core. Clients über WARP, Router-Infra über Liwest (Policy-Route via Routing-Regel → Routing-Tabelle warpout).
  • Verworfen: WARP-Client als RouterOS-Container (kein /dev/net/tun); Mesh-Node als Egress (egressed über die eigene Public-IP = die blockierte); cloudflared (reiner Ingress); Default-Route-Flip (würde die WG-Transporte von wg-hub/cf-mesh/warpout mitreißen → fm/dw kaputt, Hairpin).
  • Nicht-Ziel: Dedicated Egress (paid) — bewusst „vorerst" geteilte CF-IP.

3. Ist-Stand (live 2026-09-25)

  • crs326-core (ROS 7.24.1): WG cf-mesh (Ingress 100.96.0.6/32) + wg-hub (FM/DW). Kein Container am Core.
  • APs fahren cfd-min-arm64-cloudflared-Container (bf-public, ~75 MB RAM, Flash nur ~54 MB frei) → nicht anfassen.
  • Firewall deny-by-default beide Ketten (TP5) → Egress braucht explizite forward-Regel.
  • Routing: Default 0.0.0.0/0 → 90.146.33.1 (vlan100). Nur Routing-Tabelle main, keine Mangle-Marks.

4. CF-MCP: Account verifizieren (read-only, Schritt 0)

Mit mcp__cloudflare__execute gegenlesen (bestätigt 2026-09-25): warp_connector = nur bf-core-mesh (Mesh, nicht Client) · egress_cidr_pairs = [] (kein Dedicated Egress). → es gibt noch keinen WARP-Client.

⚠️ Grenze des CF-MCP: die Consumer-WARP-Registrierung (PrivateKey + Reserved-IPs) läuft über api.cloudflareclient.com, NICHT über die Account-API — dafür gibt es keinen CF-API-Endpoint. Der CF-MCP kann den Account verifizieren, aber den Client-Key nicht erzeugen. Dafür §5 (wgcf), genau einmal.

5. Einmalig: Consumer-WARP-Key (wgcf, nicht wgcf-connector)

docker run --rm -v $(pwd):/app/output ghcr.io/animmouse/wgcf register
docker run --rm -v $(pwd):/app/output ghcr.io/animmouse/wgcf generate   # → wgcf-profile.conf
  • ⚠️ wgcf (Consumer) ≠ wgcf-connector (Mesh). Das Repo hat bisher nur den Connector benutzt.
  • .conf liefert: PrivateKey, Address = 172.16.0.2/32 (+ v6 → ignorieren, v4-only), Peer-PublicKey bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo= (= gleicher CF-Wellknown-Key wie Mesh), Endpoint engage.cloudflareclient.com:2408 → 162.159.192.x/.193.x, MTU 1280.
  • PrivateKey → ~/.secrets/mcp/cloudflare/.env (CF_WARP_CLIENT_PRIVKEY, chmod 600, nie in Git).

6. RouterOS — MCP-first

Primärpfad: mcp__mikromcp__plan_changes (dryRun, reviewen) → mcp__mikromcp__apply_plan. Ausnahme (WG): MikroMCP kann WG-PrivateKey nicht, endpoint="IP:Port" wird falsch zerlegt, persistent-keepalive fehlt (TP16.1 §4.3) → diese 4 Zeilen einmal via sshpass -e (Key aus .env interpoliert, nicht im Literal; Fehlerausgabe sed-maskieren — CLAUDE.md-Regel).

# (a) sshpass — WG-Bring-up (Interface+Key, Peer mit endpoint/keepalive)
/interface wireguard add name=warpout mtu=1280 private-key="<CF_WARP_CLIENT_PRIVKEY>" comment="WARP Egress (privat)"
/interface wireguard peers add interface=warpout name=warp-cf-pop \
    public-key="bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=" \
    endpoint-address=<162.159.192.x|193.x> endpoint-port=2408 \
    allowed-address=0.0.0.0/0 persistent-keepalive=25s
# (b) MCP plan_changes→apply_plan — alles ohne Secret
/ip address add address=172.16.0.2/32 interface=warpout comment="WARP client (v4)"                    # manage_ip_address
/routing table add name=warpout fib                                                                   # manage_routing_table
# lokale Routen in warpout — PFLICHT, sonst läuft VLAN↔VLAN über den Tunnel!
/ip route add dst-address=10.40.10.0/24 gateway=vlan10 routing-table=warpout                          # manage_route (local)
/ip route add dst-address=10.40.20.0/24 gateway=vlan20 routing-table=warpout                          # manage_route (local)
#   … analog vlan30/vlan40/vlan50/vlan90 (die exakten /24 aus den Connected-Routes übernehmen)
/ip route add dst-address=0.0.0.0/0 gateway=warpout routing-table=warpout                             # manage_route (Internet via WARP)
# Routing-Regel je Client-VLAN — „die Ausnahme direkt über den WG-Tunnel", kein Mangle
/routing rule add interface=vlan10 action=lookup-only-in-table table=warpout comment="Client -> WARP"  # manage_routing_rule
/routing rule add interface=vlan20 action=lookup-only-in-table table=warpout
#   … analog vlan30/vlan40/vlan50/vlan90
/ip firewall nat add chain=srcnat out-interface=warpout action=masquerade comment="WARP egress"        # kein NAT-Tool → run_command
/ip firewall filter add chain=forward in-interface=vlan10,vlan20,vlan30,vlan40,vlan50,vlan90 \
    out-interface=warpout action=accept comment="WARP egress fwd"                                      # manage_firewall_rule
  • Warum Routing-Regel statt Default-Route-Flip: main-Default bleibt 90.146.33.1 → der WG-Transport von warpout/cf-mesh/wg-hub (Endpunkt-IPs) und die Router-eigenen Dienste (DNS-DoH, NTP) laufen weiter über Liwest. Nur Client-Ingress (VLANs) wird umgeleitet → kein Hairpin, fm/dw und cf-mesh bleiben unberührt.
  • Scope: alle Client-VLANs (10/20/30/40/50/90). vlan99 (Mgmt) + vlan100 (WAN) bewusst nicht umgeleitet.
  • DNS: bleibt auf dem Core (DoH→CF-Gateway, TP23); nur der Verbindungs-Traffic der Clients geht durch WARP.

7. Nautobot / Golden Config

  1. Vorher /system backup → pre-warp-egress-<datum>.backup (TP22-Regel 3).
  2. WG-Bring-up direkt (Ausnahme #4-Muster: Privkey nicht GC-deploybar); Route/Mangle/NAT/Firewall/Adresse normal.
  3. Ist-Stand → Nautobot intended config: Privkey als <<REDACTED>> (byte-identisch zur ConfigReplace-Maske).
  4. Compliance-Features ggf. um neue Sektionen erweitern, dann --refresh-compliance → grün, keine Drifts.
  5. TP-Anlage (nach User-Okay): Gitea-Issue + ### TPnn in docs/teilprojekte.md.
  6. Diese Datei am Ende auf ✅ AUSGEFÜHRT + As-Built-Werte setzen.

8. Verifikation

  • Beliebiges Client-Gerät: curl -s https://cloudflare.com/cdn-cgi/trace → warp=on, ip=104.28.x.x (nicht 90.146.33.177).
  • Cannaleo-Test: curl -s -o /dev/null -w "%{http_code}\n" https://blaueblueten.de/ → 200 (vorher 403).
  • Regression (wichtig!): wg-hub-Peers fm/dw + cf-mesh-Peer cf-pop weiterhin last-handshake < 60s; WARP-Zugriff aufs BF-Netz (Mesh) weiter ok. Beweist: Router-Infra blieb auf Liwest, nichts mitgerissen.
  • warpout-Peer: last-handshake < 10s, rx/tx laufen.

9. Rollback

  • Routing-Regeln disable (Clients fallen auf main-Default = Liwest zurück) ODER warpout-Interface disable.
  • Router-Infra war nie betroffen → fm/dw/cf-mesh unberührt. Reversibel; Backup §7.1.

10. Gotchas

  • Kein Container am Core (arm 32-bit, 2,9 MB frei) — natives WG ist kein Container: ein paar Config-Zeilen. AP-Flash-Enge (54 MB) ist hier nicht im Spiel.
  • MikroMCP-WG-Lücken (TP16.1 §4.3): PrivateKey, endpoint-Zerlegung, persistent-keepalive → sshpass.
  • Name ohne Bindestrich (warpout) für GC-Tauglichkeit.
  • WARP = geteilte CF-IP: verbirgt die Heim-IP, ersetzt aber nicht „sauber" (manche Seiten flaggen VPN/CF-IPs).
  • Kein Auto-Failover: stirbt WARP (Handshake weg), verlieren Clients den Internetzugang (Mark bleibt, Interface ist L3-mäßig „up"); die Router-Infra läuft weiter über Liwest → Router bleibt per MikroMCP erreichbar, Rollback §9. Netwatch-Failover = optional, später.
  • Verifikation über TCP/HTTP (curl), nicht nc -z (trügt hinter Proxys, TP16.1 §4.5).

11. Offen vor Start

  1. TP-Nummer + Gitea-Issue (User-Okay) + Prosa-Abschnitt.
  2. Consumer-WARP-Konto (wgcf register = neues Wegwerf-Konto, geteilte IP, bewusst).