Files
bf-infra/docs/tp16.1-warp-mesh-native-ap1.md
Christoph Emsenhuber 97e2d9c354 docs 🔎 TP16.1: Recherche verifiziert + Runbook gehärtet (Mesh-Rebranding, ICMP-Proxy-Prereq, vnet-Gotcha, v4-only)
Primärquellen (animmouse Mesh-on-MikroTik + wgcf-connector) und offizielle
CF-Docs/OpenAPI-Spec gegengelesen — Lead bestätigt, 6 Härtungen eingearbeitet:

- Produkt seit 2026-04-14 offiziell 'Cloudflare Mesh' (API bleibt /warp_connector,
  im Spec bestätigt inkl. Token- und HA-Endpoints; Node-Limit 50/Account)
- NEU: ICMP-Proxy-Prerequisite — Dashboard-only (nicht im API-Spec), 1 Klick
  Traffic settings→Proxy→ICMP, sonst bleibt das Abnahmekriterium Ping tot
- Split-Tunnel-Exclude auf BEIDEN Device-Profilen (Default + Mesh)
- vnet-Gotcha beim Route-Umzug ('virtual networks only for cloudflared') →
  Weg A PATCH / Weg B DELETE+POST; CF-API-Writes sind WARP-unabhängig (Rollback safe)
- v4-only-Entscheid (Mesh-IPv6-site-to-site laut Guide broken, kein NAT66)
- keepalive 1m ok, .conf-Inhalte präzisiert (IPv6+DNS ignorieren)

Runbook ausführungsreif für die Delegations-Session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 06:07:13 +02:00

160 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# TP16.1 — Private Access via native Cloudflare Mesh (WARP Connector) auf ap1
> **Status: Konzept + Runbook fertig (2026-07-16), Recherche verifiziert + Runbook gehärtet (2026-07-17), Scharfschalten = eigene Session.**
> Selbst-tragend: eine frische Session kann das mit diesem Doc + `docs/cf-tunnel-mapping.md` straight bauen.
> SSoT-Kontext: `docs/cf-tunnel-mapping.md` §13/§14, Memory `project_cf_tunnel_1033`.
> Härtungs-Delta 2026-07-17: ICMP-Proxy-Prerequisite (§4.1.5, Dashboard-only!), Split-Tunnel auf BEIDEN Profilen (§4.1.4),
> vnet-Gotcha + Weg B beim Route-Umzug (§4.4), v4-only-Entscheid (IPv6-site-to-site laut Guide broken), Quellen verifiziert (§2).
## 1. Warum überhaupt / Root Cause
**Incident 2026-07-16 (unterwegs):** keine BF-IPs über WARP. Diagnose:
- **TCP über den CF-WARP-Pfad funktioniert** (SSH, alle Web-UIs, MCP-Endpoints erreichbar) —
- **ICMP ist strukturell kaputt.** Dreiländer-Ping-Befund: fm **36 % Loss** (ICMP-Proxy über QUIC-Connector,
funktioniert aber mies), bf **100 %** (macmini-Connector läuft seit dem http2-Fix von heute = QUIC-los →
ICMP-unfähig; zusätzlich cloudflared-Container ohne Ping-Sockets), dw **100 %** (192.168.0.0/16 steht in der
WARP-Split-Tunnel-Exclude → Pings betreten den Tunnel gar nicht).
**Kernursache:** cloudflared ist **kein** WireGuard, sondern ein QUIC/HTTP2-L7/L4-Proxy. ICMP darüber ist
Zusatz-Gefrickel (Account-ICMP-Flag + QUIC-Transport + `ping_group_range`-sysctl je Host) und war nie
zuverlässig eingerichtet. **Der WARP-Client selbst spricht WireGuard** — die passende Gegenstelle für einen
*sauberen* privaten L3-Zugang ist deshalb **Cloudflare Mesh (WARP Connector)**, ebenfalls WireGuard.
## 2. Entscheidungsweg (was verworfen wurde und warum)
- **WG-Roadwarrior (eigene WG-Peers je Gerät):** ❌ verworfen — User will KEINE Extra-App/kein Profil pro Gerät.
„Ein Client = WARP für alles" bleibt Anforderung.
- **Alle ZT-Anbieter mit Helper-Host** (CF WARP Connector als Daemon-Host, Tailscale, NetBird, Twingate):
brauchen dauerhaft einen Linux-Host → verletzt „Router = einziger SPOF, keine Abhängigkeit zu anderen Rechnern".
- **Connector als RouterOS-Container:** ❌ nicht möglich — RouterOS-7-Container haben **kein `/dev/net/tun`-
Passthrough**; der WARP-Connector-Daemon braucht ein TUN-Device. Gilt auf **jeder** Router-HW (hAP ax²/ax³/CRS/RB5009).
→ **hAP ax³ löst das NICHT** (und ist zudem Wi-Fi 6, nicht 7 — kein WLAN-Gewinn ggü. ax²). HW-Kauf ist vom
Tunnel-Fix **entkoppelt** (separat unter TP15 zu bewerten, falls 2,5G/USB/CPU gewünscht).
- **✅ Gewählt: Cloudflare Mesh als NATIVES WireGuard-Interface auf ap1.** Cloudflare Mesh spricht WireGuard,
RouterOS kann natives WireGuard (der TP10-Hub *ist* schon einer). Ein Mesh-Node wird als WG-Peer zu einem
CF-PoP registriert — **kein Container, kein TUN, kein LXC, keine neue HW.** Erfüllt „ap1 = einziger SPOF"
am besten, keine macvlan-Blindheit (ap1 routet alle VLANs nativ L3), ICMP/TCP/UDP nativ.
Quelle/Vorlage: animmouse „Setup Cloudflare Mesh on MikroTik" + „Setup Cloudflare Mesh using WireGuard".
Ehrliche Einschränkung: **von uns noch nicht gebaut** — starker, technisch schlüssiger Lead; echter Beweis = bauen+pingen.
**✅ Recherche verifiziert (2026-07-17, Primärquellen gelesen + offizielle CF-Docs + OpenAPI-Spec):**
- **Produkt-Status:** „WARP Connector" heißt seit **2026-04-14 offiziell „Cloudflare Mesh"** (Rebranding, kein Migrationszwang);
Node-Limit **50/Account**; neues Dashboard unter **Networking → Mesh** (interaktive Map, Route-Config, Setup-Wizard).
Die API-Pfade heißen weiterhin `/accounts/{id}/warp_connector` — im OpenAPI-Spec bestätigt: POST (create),
GET `…/{tunnel_id}/token` (Register-Token), dazu sogar **HA-Endpoints** (`…/configurations` active-passive-Replicas,
`…/failover`) — für uns nicht nötig (ap1 = bewusster SPOF), aber gut zu wissen.
- **CIDR-Routes für Mesh-Nodes** laufen über dieselbe `teamnet/routes`-API wie cloudflared (`POST {network, tunnel_id: <mesh_node_id>}`,
Token-Scope `Cloudflare One Networks Write` oder `Cloudflare Tunnel Write`) — unser `CF_API_TOKEN`-Pfad passt.
- **animmouse-MikroTik-Rezept bestätigt** (alle Kommandos gegengelesen, s. §4.3): CF-Wellknown-PublicKey, Endpoint-Substitution
`162.159.192.x → 162.159.193.x` (Latenz), MTU 1420, `100.96.0.0/12 → cf-mesh`-Route. Guide nutzt `persistent-keepalive=1m`
(unser Entwurf sagte 25s — beides ok, 25s konservativer hinter NAT; wir sind Public-Edge → 1m reicht).
- **IPv6-Befund (animmouse):** Mesh-site-to-site-IPv6 war beim Guide-Test **broken** (NAT66-Workaround nötig) → **wir bauen bewusst v4-only**
(kein v6 auf `cf-mesh`, kein NAT66 — BF nutzt intern eh kein v6, s. Memory `project_ipv6_state`).
- **Sources:** [Setup Cloudflare Mesh on MikroTik](https://www.animmouse.com/p/setup-cloudflare-mesh-on-mikrotik/) ·
[Setup Cloudflare Mesh using WireGuard](https://www.animmouse.com/p/setup-cloudflare-warp-connector-using-wireguard/) ·
[wgcf-connector (GitHub)](https://github.com/AnimMouse/wgcf-connector) ·
[CF-Changelog „Introducing Cloudflare Mesh"](https://developers.cloudflare.com/changelog/post/2026-04-14-cloudflare-mesh/) ·
[CF-Docs Mesh Routes](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-mesh/routes/)
## 3. Ist-Stand Account (gemessen 2026-07-16, read-only)
- Team-Domain: **`emsenhuber.cloudflareaccess.com`** → Mesh-Node-Identität = `warp_connector@emsenhuber.cloudflareaccess.com`
- **Noch KEIN** `warp_connector`-Tunnel im Account (`GET /accounts/…/warp_connector` = []).
- Device-Settings: **`use_zt_virtual_ip: false`** (Unique-Device-IP AUS — muss für Mesh AN).
- Split-Tunnel-Modus = **Exclude**; Exclude-Liste enthält u.a. **`100.64.0.0/10`** (schluckt die Mesh-Overlay-Range
`100.96.0.0/12` → Mesh-Traffic würde aktuell NICHT durch WARP gehen). Enthält NICHT `10.0.0.0/8` → deshalb geht
`10.40.0.0/16` schon heute über WARP (= warum TCP funktioniert).
- vnet-Route `10.40.0.0/16` (vnet `1480b545…`, Route-ID **`5546dcb6-5901-4da6-8d91-b17f94703f71`**) zeigt aktuell
auf **cfd_tunnel `bf-pve`** (`f7fdf3a6…`). Das bleibt der **Fallback**, bis Mesh bewiesen ist — nichts vorher löschen.
- ap1-WireGuard IST-Stand: 1 Interface `wg-hub` (listen 51821, MTU 1412), Peers `fm` (10.101.0.0/24) + `dw` (10.95.10.0/24).
Das neue Mesh-Interface ist **separat** (outbound zum CF-PoP, kein Listen-Port) → keine Kollision.
## 4. Runbook (nächste Session — Reihenfolge)
### 4.0 Plan-Probe (billig, reversibel) — ZUERST
`POST /accounts/{account_id}/warp_connector {name:"bf-ap1-mesh"}` via `CF_API_TOKEN` (curl/Bearer — der `cloudflare`-
MCP ist read-only, s. §11 der Mapping-Doc). Erfolgt der Create → Mesh ist im Free-ZT-Plan erlaubt. Scheitert er mit
Plan-/Auth-Fehler → **Stopp, Fallback = LXC-auf-pve-WARP-Connector** (alter Plan, ebenfalls dokumentiert).
Token danach: `GET /accounts/…/warp_connector/{tunnel_id}/token` → das ist das `eyJhIjoi…In0=`-Register-Token.
### 4.1 CF-Prerequisites setzen (⚠️ account-weit, trifft LIVE-Geräte → 1× kurzer WARP-Reconnect einplanen)
1. **Unique Device IP AN:** `PATCH /accounts/…/devices/settings {use_zt_virtual_ip:true}` (Dashboard: Team & Resources →
Devices → Management → „Assign a unique IP address to each device").
2. **„Allow all Cloudflare One traffic to reach enrolled devices" AN** (gleiche Seite).
3. **Device-Profile „Cloudflare Mesh"** anlegen: Match `identity.email == warp_connector@emsenhuber.cloudflareaccess.com`,
Tunnel-Protokoll = **WireGuard** (`POST /accounts/…/devices/policy`).
4. **Split-Tunnel-Exclude anpassen** (`PUT /accounts/…/devices/policy/exclude` — ⚠️ ersetzt die GANZE Liste, erst GET,
dann editiert zurückschreiben): `100.64.0.0/10` **raus**, dafür `100.64.0.0/11` + `100.112.0.0/12` **rein**
(= 100.64/10 minus 100.96/12 → Mesh-Overlay `100.96.0.0/12` läuft nun durch WARP statt als „local" behandelt zu werden).
⚠️ **Auf BEIDEN Profilen** (Default **und** dem neuen „Cloudflare Mesh"-Profil aus Schritt 3 — per-Profile-Endpoint
`…/devices/policy/{policy_id}/exclude`); animmouse macht das explizit auf beiden.
5. **ICMP-Proxy einschalten** (Neu-Fund aus den offiziellen Mesh-Docs — Prerequisite „Gateway proxy with TCP, UDP, ICMP"):
TCP+UDP sind bei uns schon an (`gateway_proxy_enabled`/`gateway_udp_proxy_enabled` = true), **ICMP fehlt**.
⚠️ Das ICMP-Flag ist **nicht im public OpenAPI-Spec** (weder `devices/settings` noch `gateway/configuration` haben es,
2026-07-17 geprüft) → **Dashboard-Klick nötig:** Traffic policies → Traffic settings → „Proxy" → **ICMP** anhaken.
Ohne das bleibt Ping durchs Gateway tot — und Ping ist genau unser Abnahmekriterium (§4.5).
### 4.2 WireGuard-Config extrahieren (einmalig, ephemer)
Auf macmini oder pve (Docker vorhanden):
```
docker run --rm -v $(pwd):/app/output ghcr.io/animmouse/wgcf-connector <REGISTER_TOKEN>
```
→ erzeugt `wgcf-connector-<registration_id>.conf` mit: **PrivateKey**, **Address** (`100.96.x.y/32` **+ eine IPv6** —
ignorieren, wir bauen v4-only), **DNS** (CF-Resolver — ignorieren, ap1 hat eigenes DNS), **MTU 1420**, Peer-**PublicKey**
(= CF-Wellknown `bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=`), **Endpoint** (`162.159.193.x:2408`).
Registriert den Node via CF-API (macht Netz-Call), läuft danach nie wieder. **PrivateKey nach `mcp/cloudflare/.env`
o. `~/.secrets`** (nicht committen).
### 4.3 ap1 nativ konfigurieren (MikroMCP: `plan_changes` dryRun → `apply_plan`)
```
/interface wireguard add name=cf-mesh mtu=1420 private-key="<PrivateKey aus .conf>"
/interface wireguard peers add interface=cf-mesh name=cf-pop \
public-key="bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=" \
endpoint-address=162.159.193.1 endpoint-port=2408 \
allowed-address=0.0.0.0/0,::/0 persistent-keepalive=25s
/ip address add address=<100.96.x.y>/32 interface=cf-mesh
/ip route add dst-address=100.96.0.0/12 gateway=cf-mesh
```
- Endpoint: falls die Config `162.159.192.x` liefert → `162.159.193.x` nehmen (Latenz-Tuning laut Guide).
- MTU 1420 aus dem Guide; gegen unsere Hub-MTU 1412 gegenprüfen (ggf. angleichen bei Path-MTU-Problemen).
- **Kein** Extra-Static-Route für den Rückweg nötig (anders als der LXC-Plan): ap1 IST der L3-Router aller VLANs.
- Firewall: forward ist aktuell permissiv (TP5-Deny offen) → kein neues Loch nötig; falls doch ein Drop greift,
gezielte Accept `100.96.0.0/12 ↔ 10.40.0.0/16` dokumentiert ergänzen.
- ⚠️ **Governance:** „Config nur via Nautobot" (TP21) → diese ap1-Zeilen entweder über Nautobot GC abwickeln
oder als vom User genehmigte Ausnahme dokumentieren (Nachtrag in GC als Follow-up).
### 4.4 Subnetz-Route + vnet-Route umziehen
- Ziel: die CIDR-Route `10.40.0.0/16` zeigt auf den **neuen Mesh-Node** statt auf `bf-pve`.
- **Weg A (zuerst versuchen):** `PATCH /accounts/…/teamnet/routes/5546dcb6… {tunnel_id: <warp_connector_id>}` —
gleiche `teamnet/routes`-API wie cloudflared, per CF-Docs auch für Mesh-Nodes gültig.
- ⚠️ **Neu-Fund vnet-Gotcha:** CF-Docs sagen „**Virtual networks are only supported for cloudflared tunnels**" —
unsere Route hängt am vnet `1480b545…` (`vnetemsenhuber`, ist aber das **default**-vnet des Accounts). Möglich,
dass der PATCH auf einen `warp_connector`-Tunnel deshalb mit 4xx abgelehnt wird.
**Weg B (Fallback):** `DELETE` der alten Route `5546dcb6…` + `POST /teamnet/routes {network:"10.40.0.0/16",
tunnel_id:<mesh_node_id>}` **ohne** vnet-Angabe (landet im default). Kurze Lücke (Sekunden) dazwischen ist ok —
Reihenfolge DELETE→POST zwingend (CIDR-Kollision verhindert POST-vor-DELETE).
- **Rollback jederzeit:** PATCH (bzw. DELETE+POST) zurück auf `f7fdf3a6…` (bf-pve) — cloudflared/bf-pve bleibt die
ganze Zeit bestehen. **Wichtig fürs Remote-Arbeiten:** alle CF-API-Writes laufen über die öffentliche API
(api.cloudflare.com), NICHT über WARP → Rollback funktioniert auch, wenn der private Pfad gerade tot ist.
### 4.5 Verifikation (Abnahme)
- MacBook, WARP **connected**: `ping 10.40.20.105`, `ping 10.40.10.1`, `ping 10.40.99.12` → **verlustfrei**
(Regressions-Gegenprobe zu heute: fm 36 % / bf 100 %).
- TCP-Gegenprobe inkl. NAS-macvlan-IP direkt: `ssh 10.40.20.167`, `https://10.40.20.100:8006`, `10.40.20.157:3000`
(gitea) mehrfach → Beweis „keine Blindheit".
- FQDN-Smoke (Stichprobe gitea/hass/pve über `*.emsenhuber.net`) → der cloudflared-Public-Teil (3-Tunnel-HA) blieb unberührt.
## 5. Danach (Cleanup, teils ins „vertagt"-Gespräch)
- Nach Mesh-Beweis: `warp-routing` auf bf-nas/bf-pve/bf-mm ggf. auf `false` (Hygiene — Public-Tunnel bedienen keine
privaten Netze mehr). CF-vnet-Route-Historie bereinigen.
- **Vertagt (User „müssen wir noch reden, hat Zeit"):** 4 tote Tunnel (TeilzeitCharlie/coolify/johnny2/openclaw),
Ghost-Connector-Session bf-pve (arm64-Leiche vom 12.07.), `gateway-dw`-CNAME.
## 6. Risiken / Fallbacks
- Einziger Eingriff mit Nutzer-Impact = vnet-Route-Umzug → Rollback = 1 PATCH zurück auf bf-pve.
- CF-Prerequisites sind account-weit (Unique-IP-ReIP, Split-Tunnel) → 1× kurzer WARP-Reconnect; alle reversibel.
- Plan-Gate: wenn `warp_connector`-Create scheitert → LXC-auf-pve-WARP-Connector als Fallback (TUN-Passthrough
`c 10:200 rwm`, `warp-cli connector new`, ap1-Static-Route `100.96.0.0/12`→LXC).
- ax³/ZeroTier/Tailscale = separate Eval-Fußnoten, nicht Teil dieses Fixes.