# TP16.1 — Private Access via native Cloudflare Mesh (WARP Connector) auf dem CRS326 Core-Switch > # ✅ **TP16.1 ABGESCHLOSSEN — 2026-08-17** > Der private Zugang läuft über **natives WireGuard auf `crs326-core`** statt über den cloudflared-QUIC-Proxy. > **ICMP ist damit repariert: 0 % Loss auf alle BF-VLANs (vorher 100 %)** — genau das Symptom aus dem Incident > vom 2026-07-16, das dieses TP ausgelöst hat. Die öffentlichen FQDNs blieben unberührt auf dem 3-Tunnel-HA > (TP16). Auflage aus Ausnahme #4 erfüllt: **68/68 compliant, keine Drifts.** > > **Die drei Erkenntnisse, die Zeit gekostet haben und beim nächsten Mal Zeit sparen:** > 1. Der Blocker war nie der Free-Plan, sondern der **API-Token** (§4.0) — Mesh ist im Free-ZT-Plan erlaubt. > 2. **`/ip service address=10.40.0.0/16` sperrt das Router-Mgmt aus dem Mesh aus** (§4.5) — Ping geht, > Management nicht. Für `branch`/`ap1`/`ap2` noch offen. > 3. **MikroMCP taugt für WireGuard nur halb** (§4.3): kein Privkey möglich, und `endpoint="IP:Port"` wird > falsch zerlegt → kein Handshake. > > ✅ **Nachlauf am selben Tag, ebenfalls abgeschlossen:** die **Mgmt-ACL wurde via Golden Config auf alle vier > Geräte ausgerollt** (neue Compliance-Feature `ip-service`, **72/72 compliant**) — s. §4.7. Und der > Dashboard-Schalter ist **recherchiert und bewusst AUS** — s. §4.1 Schritt 2. **TP16.1 hat damit keine > offenen Punkte mehr**, außer den weiterhin getrennt geführten Fremdthemen (TP16-HA, TP5-Firewall). `POST /warp_connector` erfolgreich: > **`bf-core-mesh` = `d1da7c8a-2736-4cbb-8af1-32db5393fb9c`**, Register-Token in `mcp/cloudflare/.env` > (`CF_MESH_REGISTER_TOKEN`, gitignored, `chmod 600`). ➡️ **Die seit 2026-07-17 offene Free-Plan-Frage ist damit > beantwortet: der Free-ZT-Plan erlaubt Cloudflare Mesh. Der LXC-Fallback (§6) ist vom Tisch.** > **Am Gerät ist weiterhin nichts angefasst** (kein Backup, kein `cf-mesh`, keine Route) — nächster Schritt ist > §4.1, und der ist wegen des account-weiten WARP-Reconnects **user-gated**. > > *Historie dieses Anlaufs: 2026-08-16 blockiert auf Token-Scope (zwei fehlende Permissions, s. §4.0) → > 2026-08-17 Token neu aufgesetzt, jetzt account-owned.* `crs326-core` ist seit 2026-08-08 der produktive L3-Router und seit 2026-08-13 in Nautobot adoptiert (68/68 compliant) — die Bedingung „gated bis TP21 den CRS326 abdeckt" ist **erfüllt**. §3 am 2026-08-16 neu gemessen, §4.3 von Platzhalter auf **ausführungsreif** gezogen, **Governance-Ausnahme #4 erteilt** (WG-Zeilen direkt, weil ein Privkey über Golden Config strukturell nicht deploybar ist). Scharfschalten bleibt eine **eigene Session** — es fasst account-weite WARP-Einstellungen an. > *Historie: Konzept + Runbook 2026-07-16 · Recherche verifiziert + gehärtet 2026-07-17 · Ziel-Device von ap1 auf CRS326 retargeted 2026-07-22.* > ⚠️ **Retarget-Hinweis (2026-07-22, Stand 2026-08-16 aufgelöst):** Alles unten war ursprünglich für **ap1** geschrieben und wurde auf den **CRS326 Core-Switch** ummünzt. ~~Der CRS326 ist aktuell noch Werkszustand — Management-IP/Interface-Zuordnung TBD, §4.3 ist ein Platzhalter.~~ → **überholt:** `crs326-core` ist produktiver L3-Router mit Mgmt `10.40.99.1`, §4.3 trägt jetzt echte Werte. Grund für den Retarget: TP15-Endstand (kein RB5009, Router bleibt ap1/ap2 rein für L3/WLAN) + TP21s Modell "ganzer Kabel-Fabric via RouterOS" — der CRS326 wird ohnehin Nautobot-verwaltet, ap1 bleibt Public-Edge/WLAN-Anker. > Selbst-tragend: eine frische Session kann das mit diesem Doc + `docs/cf-tunnel-mapping.md` straight bauen — vorausgesetzt der CRS326-Bring-up (TP15) und die Nautobot-Adoption (TP21) stehen. > SSoT-Kontext: `docs/cf-tunnel-mapping.md` §13/§14, Memory `project_cf_tunnel_1033`. > Härtungs-Delta 2026-07-17 (gilt unabhängig vom Zielgerät): 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). **Korrektur:** ICMP-Proxy ist bereits AN > (fm-Ping `10.101.0.0/24` beweist es) — kein To-Do, s. §4.1.5. ## 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-/Switch-HW (hAP ax²/ax³/CRS326/RB5009 — RB5009 mittlerweile ohnehin nicht mehr geplant, s. TP15). → **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**. - **✅ Gewählt: Cloudflare Mesh als NATIVES WireGuard-Interface auf dem CRS326 Core-Switch** (Retarget 2026-07-22, ursprünglich ap1). Cloudflare Mesh spricht WireGuard, RouterOS kann natives WireGuard (der TP10-Hub auf ap1 *ist* schon einer, Muster überträgt sich). Ein Mesh-Node wird als WG-Peer zu einem CF-PoP registriert — **kein Container, kein TUN, kein LXC, keine neue HW.** Der CRS326 ist der zentrale Kabel-Fabric-Knoten und sieht ohnehin alle VLANs; ap1 bleibt Public-Edge/WLAN-Anker, übernimmt aber nicht mehr die Mesh-Node-Rolle. 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: }`, 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 (**neu gemessen 2026-08-16**, read-only — CF-API + MikroMCP) > Die Messung vom 2026-07-16 ist auf der **CF-Seite unverändert** geblieben; auf der **Geräteseite** hat > sich alles geändert (Core-Cutover 2026-08-08, Nautobot-Adoption 2026-08-13). Beides unten getrennt. **Cloudflare (alle Werte am 2026-08-16 gegengeprüft, identisch zu 2026-07-16):** - Team-Domain: **`emsenhuber.cloudflareaccess.com`** → Mesh-Node-Identität = `warp_connector@emsenhuber.cloudflareaccess.com` - **Noch KEIN** `warp_connector`-Tunnel (`GET /accounts/…/warp_connector` = `[]`). Wichtig: der GET **antwortet** (kein 403) — die Plan-Frage entscheidet sich also erst am `POST` (§4.0). - `devices/settings`: **`use_zt_virtual_ip: false`** → Unique-Device-IP weiterhin **AUS**, muss für Mesh AN. `gateway_proxy_enabled: true`, `gateway_udp_proxy_enabled: true` (ICMP-Flag ist Dashboard-only, s. §4.1.5). - **Genau EIN Device-Profil** (das Default-Profil `0197d1b3-d98a-7c24-b70c-a4d89df7583b`, `service_mode_v2: warp`). ➡️ Der „auf BEIDEN Profilen"-Hinweis in §4.1.4 meint deshalb: **Default jetzt, Mesh-Profil sobald es in Schritt 3 angelegt ist** — es gibt aktuell kein zweites, auf dem man etwas vergessen könnte. - Split-Tunnel-Modus = **Exclude**; die Liste enthält weiterhin **`100.64.0.0/10`** (schluckt die Mesh-Overlay-Range `100.96.0.0/12`) und **kein** `10.0.0.0/8` → `10.40.0.0/16` läuft schon heute über WARP (= warum TCP geht). Nebenbefund: `192.168.0.0/16` steht drin — das ist die Erklärung für die 100 % Ping-Loss nach **dw**, unabhängig von diesem TP. - vnet-Route `10.40.0.0/16` (vnet `1480b545…` = `vnetemsenhuber`, **default**, Route-ID **`5546dcb6-5901-4da6-8d91-b17f94703f71`**) zeigt weiterhin auf **cfd_tunnel `bf-pve`** (`f7fdf3a6…`). Bleibt der **Fallback**, bis Mesh bewiesen ist — vorher nichts löschen. ⚠️ **Neu zu wissen:** `bf-pve` fährt aktuell nur **einen** Connector (statt zwei, s. TP16-HA-Befund 2026-08-16) — der private Pfad hängt damit heute an einem einzelnen cloudflared-Prozess. Ein weiteres Argument für das Mesh, aber **kein** Ersatz für die HA-Reparatur der öffentlichen Tunnel. - `CF_API_TOKEN` ist `active` (verify OK). Ob er die separate Permission **„Cloudflare One Connector: WARP"** trägt, lässt sich nur durch den `POST` in §4.0 feststellen — genau dort schlug es am 2026-07-17 mit `10000` fehl. **Geräteseite — die alten Zeilen hier waren überholt, Neufassung 2026-08-16:** - ~~„CRS326 = Werkszustand, keine WireGuard-Config, keine Management-IP"~~ → **falsch seit dem Core-Cutover.** `crs326-core` ist der **produktive L3-Router** (RouterOS 7.19.5): Mgmt `10.40.99.1` (vlan99), Gateway `.1` in **jedem** VLAN, WAN `vlan100` mit der öffentlichen `90.146.33.177`, NAT, DHCP, DNS. - ~~„ap1-WireGuard: 1 Interface `wg-hub`"~~ → **wg-hub liegt seit 2026-08-08 auf dem Core**, nicht mehr auf ap1 (listen 51821, MTU 1412, Peers `fm` 10.101.0.0/24 + `dw` 10.95.10.0/24, beide handshaken). Das ist TP10 und bleibt unangetastet — aber es ist jetzt das **direkte Vorbild auf demselben Gerät**: das Mesh-Interface entsteht daneben, nicht auf einem fremden Blatt. - ✅ **Die offene Frage „Rückweg-Routing TBD" aus §4.3 ist damit beantwortet:** der Core *ist* der L3-Router aller VLANs, Rückverkehr aus `10.40.0.0/16` zum Mesh-Interface braucht **keine** Zusatzroute. - `crs326-core` ist seit 2026-08-13 in Nautobot adoptiert und compliance-verwaltet (68/68) — damit ist das Gate „gated bis TP21 den CRS326 abdeckt" **erfüllt**. ## 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. **🔴 STAND 2026-08-16: Plan-Probe erneut ausgeführt — identisch gescheitert, Blocker jetzt vollständig vermessen.** `POST /accounts/…/warp_connector {"name":"bf-core-mesh"}` → `{"success":false,"errors":[{"code":10000, "message":"Authentication error"}]}`, HTTP **403**. Der Token (`455e4f8e…`) ist `active`. Gegenproben mit **demselben** Token, um Scope von Plan-Gate zu trennen: | Endpoint | Methode | Ergebnis | Bedeutet | |---|---|---|---| | `warp_connector` | GET | **200** (`[]`) | Endpoint existiert für den Account, Feature nicht wegge-403-t | | `warp_connector` | POST | **403** `10000` | ⛔ §4.0 — Permission `Cloudflare One Connector: WARP` fehlt | | `cfd_tunnel` | GET | 200 | `Cloudflare Tunnel`-Scope vorhanden (TP16-Strang unberührt) | | `teamnet/routes` | GET | 200 | §4.4-Pfad grundsätzlich da (PATCH-Edit 2026-07-17 bewiesen) | | `devices/settings` | GET | **403** `9109` | ⛔ **NEU: §4.1 ist ebenfalls blockiert** — `Zero Trust`-Scope fehlt | | `user/tokens/{id}` · `tokens/permission_groups` | GET | 403 `9109` | Token kann sich selbst nicht introspizieren → Scope-Liste nur im Dashboard | ⚠️ **Der wichtigste neue Befund: das Runbook nannte bisher NUR die eine fehlende Permission (§4.0).** Die Probe zeigt, dass auch **§4.1 komplett scheitern würde** — `devices/settings` ist für den Token schon lesend dicht. Ohne diesen Nachtrag hätte die Scharfschalt-Session den Token **zweimal** nachziehen müssen. Beides in **einem** Edit erledigen. **Gegenprobe, dass es NICHT das Free-Plan-Gate ist:** derselbe `devices/settings`-GET liefert über den `cloudflare`-**MCP** (OAuth, read-only, volle Account-Sicht) sauber `200` mit `{gateway_proxy_enabled:true, gateway_udp_proxy_enabled:true, use_zt_virtual_ip:false}`. Der Account *kann* das also — nur der Token darf nicht. ➡️ **Die Free-Plan-Frage aus §4.0 bleibt damit weiterhin unentschieden**: sie beantwortet sich erst beim Retry des `POST` mit korrekt gescoptem Token. Erst ein dann *immer noch* fehlschlagender POST wäre das Plan-Gate und würde den LXC-Fallback (§6) auslösen. **✅ AUFGELÖST 2026-08-17 — Token neu aufgesetzt, alle Proben 200.** Der Weg dahin hatte zwei Stolpersteine, die man kennen muss, weil beide *aussehen* wie „Token kaputt", es aber nicht sind: 1. **Der neue Token ist ACCOUNT-OWNED** (id `9984078812bc18d4ebb6a64b570144f2`, gültig bis **2027-01-09** ⏰). Account-Tokens verifizieren an einem **anderen** Endpoint: `GET /accounts/{account_id}/tokens/verify`. ⚠️ Am alten `GET /user/tokens/verify` antwortet ein gültiger Account-Token mit **`1000 "Invalid API Token"`/401** — zeitgleich liefern *alle* echten Endpoints 200. **Nicht als abgelaufen fehldeuten.** 2. **Dashboard-Fehler `"Account tag in access policy must match tag in request uri"`** (real erlebt, mehrfach): tritt auf, wenn eine **Zone**-Permission (hier `DNS`) in dieselbe Policy-Zeile gepackt wird, die **Account**- Ressourcen trägt. Zone-Permissions brauchen eine **eigene Policy-Zeile** mit Zone-Ressourcen. Halb richtig gesetzt = Permission sichtbar, aber jede Zone `403`, weil keine Zone eingeschlossen ist. **Verifiziertes Soll-Set (alle am 2026-08-17 mit 200 gegengeprüft) — Dashboard sagt „Edit", die API sagt „Write":** | Dashboard | API-Name | Permission-ID | braucht | |---|---|---|---| | Cloudflare One Connector: WARP → Edit | `Cloudflare One Connector: WARP Write` | `5b5c774a5d174ca88d046c8889648b3f` | §4.0 | | Zero Trust → Edit | `Zero Trust Write` | `b33f02c6f7284e05a6f20741c0bb0567` | §4.1 | | Cloudflare One Networks → Edit | `Cloudflare One Networks Write` | `e2980d9241cf4939bbbd74fdc43b9651` | §4.4 | | Cloudflare Tunnel → Edit | `Cloudflare Tunnel Write` | `c07321b023e944ff818fec44d8203567` | TP16 | | Access: Apps and Policies → Edit | `Access: Apps and Policies Write` | — | Bestand | | Access: Organizations, IdP, and Groups → Edit | `… Write` | — | Bestand | | **Zone** DNS → Edit (`emsenhuber.net`) | `DNS Write` | — | Tunnel-CNAMEs | ⚠️ **Namensfalle:** `Cloudflare One Connectors Write` (Plural, Sammelgruppe) ist **nicht** die Mesh-Permission. Ebenso heißt „Cloudflare Tunnel" in neueren Listen zusätzlich „Cloudflare One Connector: cloudflared" — beide Gruppen existieren parallel. Vollständige Liste (396 Gruppen) via MCP: `GET /user/tokens/permission_groups`.
Historischer Klickpfad (2026-08-16, vor der Auflösung) **➡️ Was zu tun ist (Dashboard, nur der User — Token-Bearbeitung ist keine Agent-Aktion):** `dash.cloudflare.com` → My Profile → API Tokens → Token `455e4f8e…` → Edit → **Account**-Permissions ergänzen: 1. **„Cloudflare One Connector: WARP" → Edit** (§4.0: `POST /warp_connector` + `/token`) 2. **„Zero Trust" → Edit** (§4.1: `devices/settings` PATCH, `devices/policy` POST, Split-Tunnel PUT) 3. **„Cloudflare One Networks" → Edit** (§4.4 Routes/vnets — GET geht heute, Edit war 2026-07-17 ok; sicherheitshalber mitnehmen) Danach neuen Wert nach `mcp/cloudflare/.env` **und** `~/.secrets/credentials.env` (beide tragen `CF_API_TOKEN`).
**⚠️ Token-Permission-Gotcha (2026-07-17, real erlebt — Ursachenanalyse, weiterhin gültig):** `POST /warp_connector` schlug mit `code 10000 "Authentication error"` (HTTP 403) fehl, obwohl der `CF_API_TOKEN` gültig ist und `teamnet/routes` PATCHen kann. Ursache: Cloudflare hat beim WARP-Connector→Mesh-Rebranding (2026-04-14) eine **eigene, separate Permission-Group** eingeführt — `Cloudflare One Connector: WARP` (Read/Edit) — „Grants write access to Cloudflare Mesh nodes". Das ist **nicht** dasselbe wie `Cloudflare Tunnel Write` (nur für cloudflared) oder `Cloudflare One Connectors Write` (Sammel-Permission, deckt aber laut Doku nicht zuverlässig Mesh ab). Verifiziert gegen `developers.cloudflare.com/fundamentals/api/reference/permissions/` (aktuelle Tabelle, nicht die älteren tunnel-spezifischen Doc-Seiten, die noch die alten Namen zeigen). Im Token-Editor unter **Account**-Permissions nach **„Cloudflare One Connector: WARP"** suchen (nicht „Tunnel"), **Edit** wählen. Für §4.4 zusätzlich **„Cloudflare One Networks"** → Edit (Routes/vnets). ### 4.1 CF-Prerequisites — ✅ **ERLEDIGT 2026-08-17, bis auf einen Dashboard-Klick** | Schritt | Stand | |---|---| | 1. Unique Device IP | ✅ `use_zt_virtual_ip: true` gesetzt (PATCH `devices/settings`) | | 2. „Allow all Cloudflare One traffic to reach enrolled devices" | ✅ **entschieden: bleibt AUS** — nicht nötig (s.u.) | | 3. Device-Profil „Cloudflare Mesh" | ✅ `4d3c0f57-a717-49ac-ab92-b420e33e6380`, match `identity.email == "warp_connector@emsenhuber.cloudflareaccess.com"`, `tunnel_protocol=wireguard`, precedence 100 | | 4. Split-Tunnel-Exclude | ✅ auf **beiden** Profilen: `100.64.0.0/10` raus, `100.64.0.0/11` + `100.112.0.0/12` rein (14→15 bzw. 16→15 Einträge) | | 5. ICMP-Proxy | ✅ war schon an, nichts zu tun (s. §4.1.5) | > ✅ **Schritt 2 — recherchiert 2026-08-17, Entscheid: bleibt AUS.** Die CF-**Settings-Referenz** führt ihn unter > „Global device client settings" (also **global**, nicht per Profil — die Mesh-Get-Started-Seite behauptet das > Gegenteil, **die Docs widersprechen sich**). Verfügbar in **allen Plänen**, alle OS, Client-Mode „Traffic and > DNS". **Was er tut:** er lässt Traffic aus Mesh/WAN **zu den eingebuchten Geräten hin** — Zugriff auf > *Device-IPs*, also die **Client↔Client**-Richtung. **Unser Anwendungsfall ist die andere Richtung** > (Gerät → Mesh-Node → `10.40.0.0/16`), und der funktioniert bewiesenermaßen ohne ihn (§4.5). Die CF-Docs > nennen **keinen** Menüpfad, und es gibt **kein API-Feld** (Spec, `devices/settings`-PATCH und Profil-GET > gegengeprüft). **Nach User-Vorgabe „im Zweifel ohne Schalter" nicht gesetzt.** Erst dann relevant, wenn > Zugriff **auf ein Endgerät** statt auf das Netz gebraucht wird.
Ursprüngliche Handlungsanweisung (vor der Recherche) > 🔴 **Schritt 2 ist der einzige verbliebene Handgriff und NUR im Dashboard machbar.** Weder > `PATCH /devices/settings` noch `POST /devices/policy` kennen ein entsprechendes Feld — im OpenAPI-Spec **und** in > der GET-Antwort des Default-Profils taucht es nicht auf (gegengeprüft 2026-08-17). Es verhält sich damit genau > wie das ICMP-Flag: Dashboard-only. **CF-Docs führen es als Mesh-Prerequisite („Mesh connectivity")**, es ist also > nicht optional. Pfad: **Team & Resources → Devices → Device settings → Profil → „Allow all Cloudflare One traffic > to reach enrolled devices"** einschalten. ⚠️ Laut CF-Doc ist das eine **Profil**-Einstellung — unser Runbook > führte sie fälschlich als Account-Einstellung („gleiche Seite" wie Unique Device IP).
### 4.7 Mgmt-ACL via Golden Config auf alle vier Geräte — ✅ **ERLEDIGT 2026-08-17, `72/72 compliant`** Der `/ip service`-Fix aus §4.5 war zunächst nur am Core und **direkt** gesetzt. Nachgezogen als sauberer GC-Rollout auf **alle vier** Geräte. **Der Befund war das Gegenteil der Erwartung.** §4.5 schloss mit „für `branch`/`ap1`/`ap2` noch offen — die haben eigene `/ip service`-Beschränkungen". **Falsch:** sie hatten **gar keine** — `ssh`, `www`, `winbox`, `api`, `ftp`, `telnet` alle aktiv und **ohne** Address-Beschränkung. Die dynamischen Einträge zeigten sogar `REMOTE 100.96.0.1`, waren also längst per Mesh erreichbar. Und **`winbox` war auf allen vier offen**, auch am Core. Damit wurde aus dem erwarteten Erreichbarkeits-Fix eine **Härtung**. | | Sollzustand (einheitlich, im Template) | |---|---| | `ssh` · `www` · `winbox` · `api` | `address=10.40.0.0/16,100.96.0.0/12` | | `ftp` · `telnet` · `api-ssl` | `disabled=yes` | - **Neue Compliance-Feature `ip-service`** (`match_config=/ip service`, `config_type=cli`, `config_ordered=false`) → 17→18 Regeln, **68→72 Checks**. Ohne die Regel stünde die ACL zwar im Template, würde aber **nie verglichen** — der Rollout wäre nicht durchsetzbar. - **Ablauf regelkonform:** `/system backup` auf allen Zielen → `--job mgmt-acl` (Dry-Run) → `--apply --no-deploy` → **gestaffelter** Deploy: erst `ap2`, am Gerät gegengeprüft, dann `ap1`/`branch`/`core` → Geräte-Gegenprobe → `--refresh-compliance`. - **Kein Lockout-Risiko:** der Deploy läuft von Nautobot (`10.40.20.30`) aus, also aus `10.40.0.0/16` — in jeder gesetzten ACL enthalten. RouterOS wendet `address=` ohnehin erst auf **neue** Verbindungen an. - ⚠️ **Scope-Erweiterung, bewusst:** `ftp`/`telnet`/`api-ssl` auf branch/ap1/ap2 **abgeschaltet**. Die Alternative wäre gewesen, „telnet aktiv" als **Sollzustand** in die SoT zu schreiben. Nichts hier nutzt sie (der ALLNET-Telnet-Runner ist Archiv, das Gerät ausgebaut). - 📌 **Schreibweise:** `/ip service set ssh …` mit dem **Dienstnamen**, NICHT `[find name=ssh]` — letzteres trifft auch den dynamischen Verbindungs-Eintrag → `failure: this is configured elsewhere`. - 🧪 **Mess-Falle am Rande:** `[/ip service get … address]` in einem `:put`-Ausdruck verketten erzeugt bei **zwei** ACL-Einträgen das **Kreuzprodukt** (16 Zeilen statt 1) — `address` ist eine Liste. Zum Prüfen `/ip service print detail` nehmen, nicht String-Verkettung. Job liegt als `mgmt-acl` in `nautobot/gc-configplan.py` (idempotent, wiederholbar). > 📎 **Nachtrag zu Schritt 3/4 aus den CF-Docs (gelesen 2026-08-17):** CF empfiehlt für Mesh-*Nodes* eigentlich ein > Profil im **Include**-Modus mit `100.96.0.12` — das gilt für Nodes, die den **Cloudflare One Client** fahren. > Unser Node ist **natives RouterOS-WireGuard** und konsumiert überhaupt kein Device-Profil; für ihn zählt > ausschließlich die Routing-Config aus §4.3. Das Profil legen wir trotzdem an (Guide-konform, schadet nicht). > Relevant ist die Exclude-Korrektur für die **Client**-Geräte (MacBook/iPhone) — und die ist gesetzt.
Ursprüngliche Schrittliste ### 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 — ✅ bereits AN, KEIN To-Do** (Korrektur 2026-07-17, User): Die offiziellen Mesh-Docs listen „Gateway proxy mit TCP, UDP, ICMP" als Prerequisite. TCP+UDP sind an (`gateway_proxy_enabled`/`gateway_udp_proxy_enabled`=true); **ICMP ist ebenfalls schon angehakt** — **Beweis: `ping` ins FM-Netz `10.101.0.0/24` funktioniert** (über den fm-QUIC-Connector). Das ICMP-Flag ist nur nicht im public OpenAPI-Spec sichtbar (Dashboard-only), daher hatte ich es fälschlich als „fehlt" geführt. → **Nichts zu klicken.** Dass bf-Ping im Incident 100 % verlor lag NICHT am Flag, sondern daran, dass die macmini-Connectoren nach dem http2-Fix QUIC-los sind (cloudflared-ICMP-Proxy braucht QUIC). Mit dem nativen WG-Mesh-Node ist ICMP ohnehin nativ (kein Gateway-ICMP-Proxy im Pfad). **Trotzdem: nach dem Scharfschalten Ping gegengeprüft** (§4.5) — falls wider Erwarten doch tot, hier zuerst schauen.
### 4.4 Route-Umzug — ✅ **ERLEDIGT 2026-08-17, Weg A hat funktioniert** `PATCH /teamnet/routes/5546dcb6-5901-4da6-8d91-b17f94703f71 {tunnel_id: d1da7c8a-…}` → `success: true`. **Der befürchtete vnet-Gotcha ist NICHT eingetreten** — CF hat den PATCH auf einen `warp_connector`-Tunnel anstandslos angenommen, obwohl die Route am vnet `1480b545…` hängt. Weg B (DELETE+POST) war nicht nötig und bleibt nur als Fallback dokumentiert. `10.40.0.0/16` zeigt jetzt auf **`bf-core-mesh`** statt auf `bf-pve`. **Rollback** unverändert 1 PATCH zurück auf `f7fdf3a6…` — `bf-pve` läuft weiter, nichts gelöscht. ### 4.2 WireGuard-Config extrahieren (einmalig, ephemer) — ✅ erledigt 2026-08-17 > **As-built:** gelaufen in **LXC 400 auf pve** (x86_64; der macmini ist arm64 — bei den cloudflared-Connectoren > hatten wir dort schon eine arm64-Leiche). Ergebnis s. §4.3. Der `.conf` mit dem PrivateKey wurde nach dem > Auslesen auf dem LXC **geshreddert** (`shred -u`) — der Schlüssel liegt jetzt nur noch in > `mcp/cloudflare/.env` (`CF_MESH_PRIVKEY`, gitignored, `chmod 600`) und am Gerät. Auf macmini oder pve (Docker vorhanden): ``` docker run --rm -v $(pwd):/app/output ghcr.io/animmouse/wgcf-connector ``` → erzeugt `wgcf-connector-.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 `crs326-core` nativ konfigurieren — ✅ **AS-BUILT 2026-08-17** **Ist-Stand am Gerät (verifiziert):** | | | |---|---| | Interface | `cf-mesh`, MTU **1420**, `running`, listen-port 13279 (auto) | | PublicKey (Gerät) | `vGkiAuQt8iHGtNkllTxMxjlgQhN9RtSTjC8qHwnccAw=` | | Mesh-IP | **`100.96.0.6/32`** (v4-only, die mitgelieferte v6 `2606:4700:cf1:1000::6/128` bewusst ignoriert) | | Peer | `bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=` @ **`162.159.193.2:2408`**, allowed `0.0.0.0/0`, keepalive 25s | | Handshake | ✅ steht (`last-handshake` < 10s, rx/tx laufen) | | Route | `100.96.0.0/12 → cf-mesh`, Flags **`A s`** (active/static) | | Registrierung | `reg_id 01a00f2d-2316-12f6-90f6-c3f344469fb3`, CF-Node-Status **healthy**, 1 Connection | | Backup vor dem Eingriff | `pre-tp16.1-mesh-20260817.backup` (Auflage Ausnahme #4 ✅) | **Endpoint-Hinweis erledigt:** die Config lieferte direkt `162.159.193.2` — die im Guide beschriebene Substitution `162.159.192.x → 162.159.193.x` war **nicht nötig**. > ⚠️ **Zwei Werkzeug-Fallen, beide real getroffen — für die nächste WG-Arbeit wichtig:** > > 1. **MikroMCPs `manage_wireguard_interface` kann prinzipiell KEINEN privaten Schlüssel annehmen** > („RouterOS generates the private key on create — it is never passed in"). Bei CF Mesh kommt der Key aber > **von CF** — das Tool ist für diesen Fall also strukturell ungeeignet. Weg: `sshpass`+SSH mit > `$CF_MESH_PRIVKEY` aus der gitignoreten `.env` **interpoliert**, damit der Schlüssel weder durch den > Agent-Kontext noch ins MCP-Journal wandert; Fehlerausgabe vorher durch `sed` maskieren (Memory > `feedback_redact_router_passwords_in_tracebacks`). Peer/IP/Route liefen danach regulär über > `plan_changes` → `apply_plan`. > 2. **`manage_wireguard_peer` verarbeitet `endpoint: "IP:Port"` falsch:** es schreibt den *ganzen* String nach > `endpoint-address` und lässt `endpoint-port=0` → **kein Handshake**, rx/tx bleiben 0. Das sieht aus wie ein > Netz-/Firewall-Problem, ist aber ein Parameter-Bug. Fix: `/interface/wireguard/peers/set [find …] > endpoint-address= endpoint-port=`. **Nach jedem Peer-Add per MikroMCP `print detail` gegenlesen.** > 3. Ebenso ohne Parameter im Tool: **`persistent-keepalive`** → separat per `run_command` setzen.
Ursprünglicher Plan (Stand 2026-08-16) > Der frühere Platzhalter-Vorbehalt („Management-IP/Interface-Zuordnung fehlt") ist erledigt: Zielgerät ist > **`crs326-core`** (Mgmt `10.40.99.1`, MikroMCP-routerId `crs326-core`). Offen bleibt nur, was erst zur Laufzeit > existiert: der PrivateKey und die `100.96.x.y`-Adresse aus §4.2. ``` /interface wireguard add name=cf-mesh mtu=1420 private-key="" comment="TP16.1 Cloudflare Mesh" /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 comment="TP16.1 Mesh-Overlay" /ip route add dst-address=100.96.0.0/12 gateway=cf-mesh comment="TP16.1 Mesh" ``` - **Endpoint:** falls die Config `162.159.192.x` liefert → `162.159.193.x` nehmen (Latenz-Tuning laut Guide). - **MTU:** 1420 aus dem Guide. Direkter Vergleichswert steht jetzt **auf demselben Gerät**: `wg-hub` läuft mit **1412**. Bei Path-MTU-Symptomen (TCP baut auf, große Transfers hängen) zuerst auf 1412 angleichen. - ✅ **Rückweg-Routing — erledigt, keine Zusatzarbeit.** `crs326-core` ist der L3-Router **aller** VLANs (`vlan10/20/30/40/50/90/99` mit `.1`), Rückverkehr nach `10.40.0.0/16` läuft also ohne Zusatzroute. Das war beim ursprünglichen CRS326-Retarget die einzige echte Unbekannte. - **Namenskollision vermeiden:** auf dem Gerät existiert bereits `wg-hub` (TP10). Das neue Interface heißt `cf-mesh` — ⚠️ **Bindestrich in `name=` hat RouterOS beim GC-Push schon einmal abgelehnt** (Memory `ref_gc_push_gotchas_routeros`); beim Direkt-Write über MikroMCP war das nie ein Problem, aber falls die Zeilen später doch durch die GC laufen, ist `cfmesh` der sichere Name. - **Firewall:** die `forward`-Kette endet derzeit **ohne Drop** (TP5 steht aus) → der Mesh-Verkehr passiert ohne Zutun. ⚠️ **Für TP5 vormerken:** sobald deny-by-default greift, braucht es explizit `forward accept 100.96.0.0/12 ↔ 10.40.0.0/16`, sonst kappt die Firewall den privaten Zugang. Gehört in die TP5-Whitelist, nicht hierher. - ✅ **Governance — Ausnahme #4 erteilt (User, 2026-08-16).** Die frühere Zeile („läuft regulär über Nautobot GC") trägt **nicht mehr**: TP22-Regel 1 schließt jede Sektion mit `<>` generell vom Push aus, und `/interface wireguard` ist genau so eine Sektion — ein Privkey ist über GC gar nicht deploybar. Diese vier Zeilen dürfen daher **direkt** (MikroMCP `plan_changes` dryRun → `apply_plan`). **Auflage wie bei Ausnahme #2:** danach Ist-Stand als intended config zurück nach Nautobot. **Scope:** nur der Mesh-Bring-up auf `crs326-core`, nicht spätere WG-Änderungen, nicht andere Geräte. Vorher `/system backup` (TP22-Regel 3).
#### Und wie hält man Secrets langfristig aus der Config raus? Die Ausnahme soll ein **einmaliger Bootstrap** bleiben, kein Dauerzustand. Der Denkfehler, den man vermeiden muss, ist, einen privaten Schlüssel als *Konfiguration* zu behandeln — er ist **Geräte-Identität**, wie ein SSH-Hostkey. Die branchenübliche Regel: > Der private Teil entsteht auf dem Gerät und verlässt es nie. Nur der **öffentliche** Teil geht in die Source of > Truth — und wird von dort in die Configs der **Gegenstellen** gerendert. | Was | Klasse | Wo verwaltet | |---|---|---| | PrivateKey | Geräte-Identität | nur am Gerät — nie in Git, nie in der intended config, nie in einem ConfigPlan | | PublicKey | öffentliche Information | Nautobot (Custom Field/Data) → in die Peer-Configs gerendert | | `listen-port`, `mtu`, `comment`, Peers, `allowed-address`, Routen, Firewall | Konfiguration | Golden Config, ganz normal | RouterOS unterstützt das direkt: `/interface wireguard add` **erzeugt den Privkey selbst**, `print` gibt den Pubkey aus. Compliance bleibt trotzdem grün, weil die `ConfigReplace`-Regel aus TP22 Schritt 0 den Privkey im Backup auf **byte-identisch dieselbe** Maske normalisiert, die das Template emittiert. ⚠️ **Vorbehalt genau hier:** bei Cloudflare Mesh erzeugt **CF** das Schlüsselpaar (§4.2 holt es per `wgcf-connector`). Wenn der Flow keinen eigenen Pubkey akzeptiert, fällt die On-Device-Erzeugung weg — das Prinzip („nie in Git, nie in der intended config") hält trotzdem, der Key wird dann einmalig eingespielt und lebt danach nur am Gerät. **Stufe 2, falls Secrets doch verwaltet werden müssen** (mehr Geräte, Rotation, PSKs): `nautobot-secrets-providers` nachinstallieren (Vault/AWS SM/Azure KV/1Password; Core kann schon Env-Var + Text-File) und den echten Wert erst **bei der ConfigPlan-Erzeugung** einsetzen — gespeicherte intended config behält den Platzhalter, der Plan wird nach dem Deploy gelöscht. Preis: Klartext liegt transient in Postgres, braucht Retention-Disziplin. Bei vier Geräten und einem Key lohnt das nicht; **das ist ein TP für die Zeit nach TP18/TP20.1.** *(Nicht verwechseln: `CredentialsNautobotSecrets` + SecretsGroup „RouterOS Nornir" sind schon im Einsatz — das sind die **Login**-Credentials für Nornir, nicht Secrets *im Config-Inhalt*.)* **Sofort fälliger Modellierungs-Nachzug, unabhängig vom Mesh:** die WG-Peers `fm` und `dw` stehen heute mit hartkodierten `public-key="…"`-Werten im Jinja-Template (`nautobot/mikrotik_routeros.j2`). Public Keys sind Daten, keine Templatezeilen — die gehören nach Nautobot und von dort gerendert. ### 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: }` — 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:}` **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 — ✅ **BESTANDEN 2026-08-17** (MacBook am iPhone-Hotspot, WARP connected) **Ping — der eigentliche Zweck des TPs. Vorher 100 % Loss, jetzt:** | Ziel | VLAN | Loss | avg RTT | |---|---|---|---| | `10.40.10.1` | 10 | **0 %** | 47 ms | | `10.40.20.105` (HA) | 20 | **0 %** | 56 ms | | `10.40.99.12` (ap2) | 99 | **0 %** | 57 ms | | `10.40.20.10` (NAS) | 20 | **0 %** | 63 ms | | `10.40.99.1` (core) | 99 | **0 %** | 58 ms | **TCP-Gegenprobe (echter Datenfluss, nicht nur Connect):** `https://10.40.20.100:8006` → 200 · `http://10.40.20.30:8080` → 302 · `ssh macmini` → ok. **FQDN-Smoke** (gitea/hass/pve/nautobot/vw über `*.emsenhuber.net`): 200/200/302/302/200 in 0,2–0,3 s → **der cloudflared-Public-Teil blieb unberührt.** *(`10.40.20.10:5000/5001` antwortet nicht — die DSM-Web-Ports lauschen dort schlicht nicht; SSH und 443 auf demselben Host sind offen. Kein Mesh-Befund.)* > 🔴🔴 **DIE FALLE DIESES TPs — `/ip service` sperrt das Router-Management aus dem Mesh aus.** > > Unmittelbar nach dem WARP-Connect war **`crs326-core` mgmt-seitig tot**: MikroMCP brach mit > `Connection lost before handshake` ab, SSH mit `kex_exchange_identification: Connection reset`, REST mit > **HTTP 502** — **während Ping zu genau derselben IP verlustfrei lief** und alle Hosts *hinter* dem Core > (pve, Nautobot, macmini) normal antworteten. > > **Ursache:** auf dem Core steht `/ip service set ssh,www address=10.40.0.0/16`. Der Mesh-Client kommt aus > **`100.96.0.0/12`** und fällt aus der ACL. ICMP kennt keine Service-ACL → geht. Und `nc -z` meldete > „offen", weil der **CF-Gateway-Proxy** den TCP-Handshake terminiert, bevor das Origin überhaupt antwortet — > ein TCP-Connect ist über WARP also **kein** Reachability-Beweis. > > **Fix:** `/ip service set [find name=ssh] address=10.40.0.0/16,100.96.0.0/12` (analog `www`). > Danach SSH und REST über den Mesh sauber. **Merkregel: wer per Mesh ans Netz kommt, ist noch lange nicht > am Router.** Jede Mgmt-ACL, die auf `10.40.0.0/16` steht, muss die Mesh-Range mitnehmen — > für `crs326-branch`/`ap1`/`ap2` ist das noch **offen** (die haben eigene `/ip service`-Beschränkungen). > > ⚠️ Zwei Fehlspuren, die ich unterwegs verfolgt habe und die man sich sparen kann: (1) **MTU** — große Pings > ab ~1350 B scheitern, das ist aber das **WARP-Client-Interface** (`utun` MTU 1380), nicht `cf-mesh`; > normaler TCP-Verkehr ist davon nicht betroffen. (2) **SSH-Session-Erschöpfung** — naheliegend bei > `Connection reset`, war es aber nicht. Der Test, der es entschied: **Banner-Grab vom macmini aus** > (`nc 10.40.99.1 22` → `SSH-2.0-ROSSSH`) — von innen gesund, also Pfad/ACL statt Gerät. > > ℹ️ Nebenbefund beim Setzen: `/ip service set [find name=ssh] …` trifft **auch den dynamischen > Verbindungs-Eintrag** (`D c ssh … local 10.40.99.1`) und quittiert mit `failure: this is configured > elsewhere`. **Harmlos** — der statische Eintrag wird trotzdem gesetzt. Nicht als Fehlschlag werten. ### 4.6 Auflage Ausnahme #4 — ✅ **ERLEDIGT 2026-08-17: Ist-Stand ist intended config, 68/68 compliant** Ablauf: `--refresh-compliance` zeigte zuerst **65/68** mit drei `extra`-Drifts (interface-wireguard, ip-address, ip-route) — exakt die Mesh-Zeilen. Die Zeilen **byte-genau aus dem Nautobot-Backup** (nicht aus dem Runbook!) ins Jinja-Template übernommen, ins GC-Repo `bf-golden-config` gepusht, Repo-Sync, erneut compliance: **68/68, keine Drifts.** - Der Privkey bleibt draußen: das Template emittiert `private-key="<>"` — byte-identisch zur `ConfigReplace`-Maske aus TP22 Schritt 0, genau wie bei `wg-hub`. Secret-Gegenprobe vor jedem Push gelaufen. - ⚠️ **Gotcha beim Push:** Nautobot schreibt Backups *und* intended configs in **dasselbe** Repo → der zweite Push wurde `rejected (fetch first)`, weil die Compliance-Läufe dazwischen committen. `git pull --rebase` vor jedem Push einplanen. - `/ip service` steht **nicht** im Template → die ACL-Erweiterung erzeugt keine Drift, ist damit aber auch **nicht** in der SoT abgebildet. Bewusst so belassen; gehört zu TP5, wenn die Mgmt-Ebene modelliert wird.
Ursprüngliche Abnahme-Kriterien ### 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. --- ## Nachtrag 2026-08-25 — „Connector als RouterOS-Container ❌" galt dem TUN-Bedarf, nicht jedem Container §2 schließt „Connector als RouterOS-Container" aus, weil **RouterOS-7-Container kein `/dev/net/tun` durchreichen** und der WARP-Connector-Daemon ein TUN-Device braucht. **Das stimmt weiterhin — für den WARP-Connector.** 🔴 **Es wurde damals aber nicht getrennt, und die Verallgemeinerung hat sich festgesetzt:** **`cloudflared` braucht kein TUN.** Es ist ein Userspace-HTTP/QUIC-Proxy und läuft in einem RouterOS-Container problemlos — auf arm64 oder x86, `container`-Paket und `device-mode` vorausgesetzt. **Live gemessen 2026-08-25:** ap1/ap2 sind **arm64** mit **95 MB freiem Flash** und **690 MB freiem RAM** — genug für einen Connector. `crs326-core` ist **arm 32-bit mit 2,9 MB frei von 16 MB** und kommt für Container **nie** in Frage. ⇒ Die Zeile „verletzt *Router = einziger SPOF, keine Abhängigkeit zu anderen Rechnern*" trug für den privaten Strang — für den **öffentlichen** hat sie nie jemand geprüft. Genau das holt **TP31** nach.