Files
bf-infra/docs/tp16.1-warp-mesh-native-crs326.md
Christoph Emsenhuber bb2c87c68e Compliance 68/68 + TP24 (USV) ausgeloest + Doku auf den Stand nach dem Core-Cutover
Zwei Straenge, die sich dieselben Dateien teilen — deshalb ein Commit.

1) COMPLIANCE-DRIFTS BESEITIGT (User: "beseitige autonom alle") -> 68/68, 0 Drifts

Entfernt wurden die 4 verbliebenen `extra`-Drifts (der fuenfte, core 10.40.41.211,
war schon ueber TP19-vlan1-core-address gefallen):
- ap1 + ap2: Werks-defconf `/ip dns static router.lan -> 192.168.88.1`. Auf beiden
  APs der einzige statische Eintrag, die IP existiert im Netz nicht. Der Core hat
  sein eigenes, korrektes router.lan -> 10.40.99.1 (im Template, compliant).
- crs326-branch: Bring-up-Rest SVI vlan10 + 10.40.10.2, Adresse zuerst.

Richtung bewusst "am Geraet entfernen" statt "ins Template aufnehmen" — genau so
steht es seit 2026-08-13 im Switch-Zweig des Templates. Der frueher notierte
Vorbehalt "potenzielle Rettungspfade" ist per User-Entscheid aufgehoben; der
Mgmt-Fallback aus VLAN10 auf dem branch entfaellt damit (Rueckweg = MAC-Winbox am
selben Draht).

Weg: 3 manuelle GC-ConfigPlans, kein Direkt-Write. Vorher /system backup je Geraet.
Deploy gestaffelt — erst die APs, am Geraet gegengeprueft, dann branch.

Verifikation am GERAET, nicht am Job: DNS-Statics total=0 bei unveraenderten 28
bzw. 3 WLAN-Clients; branch nur noch vlan99 + 10.40.99.2/24; Ping vom Core 4/4.
Entscheidende Gegenprobe: /interface bridge vlan/export byte-identisch — `bridge`
steht dort statisch als tagged-Member von VLAN10, das Entfernen des SVI hat also
weder Traffic-Wirkung noch neuen Drift. Danach Backup -> Intended -> Compliance,
alle 4 Geraete mit frischem Success-Datum.

Neue API-Falle, die erklaert warum es bisher kein Skript gab:
POST /plugins/golden-config/config-plan/ ist 405 — das ViewSet ist read-only.
ConfigPlans entstehen nur ueber den Job "Generate Config Plans" (plan_type=manual
+ commands-TextVar). plan_type=missing erzeugt ausserdem nur add-Zeilen, also muss
alles Entfernende zwangslaeufig ein manueller Plan sein.

Neu: nautobot/gc-configplan.py — loest Device-UUIDs einzeln per Name auf und
assertet plan.device UND config_set gegen das Ziel (die alphabetische
Sortier-Falle aus TP21.1 kann damit nicht wiederkommen), verweigert config_sets
mit <<REDACTED>> (Regel 1) und print-Zeilen, kann gestaffelt deployen
(--deploy-ids) und kapselt Backup -> Intended -> Compliance (--refresh-compliance).
Dry-Run ist Default.

2) TP16.1: GATE OFFEN, Runbook ausfuehrungsreif, Governance-Ausnahme #4

Die Bedingung "gated bis TP21 den CRS326 abdeckt" ist seit der Adoption am
2026-08-13 erfuellt. §3 neu gemessen, §4.3 von Platzhalter auf echte Werte.
Die letzte inhaltliche Unbekannte hat sich erledigt: "Rueckweg-Routing TBD" —
der Core IST L3-Gateway aller VLANs, es braucht keine Zusatzroute. wg-hub liegt
auf demselben Geraet und ist damit direktes Vorbild statt fernes Muster.

Klarstellung, weil leicht zu verwechseln: das Mesh ersetzt die Tunnel NICHT. Es
loest nur den privaten Zugang (10.40.0.0/16 per IP, inkl. ICMP); die oeffentlichen
FQDNs bleiben auf cloudflared. Relevant dabei: die Route 10.40.0.0/16 zeigt auf
bf-pve — und der faehrt derzeit nur einen Connector.

Ausnahme #4 (User 2026-08-16): die WG-Zeilen des Mesh-Bring-ups auf crs326-core
duerfen direkt gesetzt werden. Grund ist strukturell, nicht bequem — TP22-Regel 1
schliesst jede <<REDACTED>>-Sektion vom Push aus, ein Privkey ist ueber Golden
Config gar nicht deploybar. Auflage: Backup vorher, intended config danach zurueck
nach Nautobot. Scope: nur dieser Bring-up.

Damit das ein einmaliger Bootstrap bleibt, ist das Secret-Handling-Design
dokumentiert: ein privater Schluessel ist Geraete-Identitaet, keine Konfiguration
(wie ein SSH-Hostkey). Der private Teil entsteht am Geraet und geht nie weg, nur
der oeffentliche kommt in die SoT und wird in die Peer-Configs gerendert.
Stufe 2 (nautobot-secrets-providers + Vault) lohnt bei 4 Geraeten und einem Key
nicht. Nebenbefund: die WG-Peers fm/dw stehen mit hartkodierten public-keys im
Jinja-Template — Public Keys sind Daten, keine Templatezeilen.

3) TP24 — USV aus TP15 herausgeloest (User-Entscheid)

Die DC-USV war der letzte offene Rest in TP15 und hat mit Switching/Routing
sachlich nichts zu tun (eigene Einkaufs-, Verkabelungs- und Elektrik-Logik).
TP15 ist damit inhaltlich zu. TP24 traegt jetzt 48V-Bus, MeanWell/LiFePO4,
Laufzeiten, DC-Eingangs-Tabelle und die 5 offenen Entscheidungen — darunter neu
die Standortfrage, die sich durch den Core-Umzug ins UG neu stellt.

4) DOKU-AUFRAEUM-SESSION A-E abgearbeitet

- Ist-Stand-Block neu geschrieben, live gegen die Geraete geprueft. Alte Fassung
  als ausklappbare Historie, ⚠️-Provisorium entfernt.
- Reihenfolge-Abschnitt auf den echten Rest-Pfad TP5 -> TP6 eingedampft (Punkt 3
  sagte noch "Jetzt: Omada-PVID-Fix").
- Stale ap1-als-Router-Aussagen in TP3/TP4/TP15/TP16.1/TP21/TP-R korrigiert —
  fortgeschrieben, nicht ueberschrieben.
- CLAUDE.md: Mac mini .41.167 -> .20.167 (war stale, live per ARP bestaetigt),
  branch-Uplink "sw1 GE8" -> "core ether8", ALLNET-/Omada-Gotchas als Historie.
- switch/ als Archiv gekennzeichnet (Geraet ausgebaut, Runner nicht ausfuehrbar).
- omada-mcp + CF-Ingress live gegengeprueft: beides sauber, Punkt abgehakt.

Nebenbefund, eingetragen aber nicht angefasst: das CF-Tunnel-HA ist degradiert
(bf-nas und bf-pve mit je nur einem Connector), und vier tote Alt-Tunnel tragen
VLAN1-Origins — darunter die Aufloesung des "unidentifizierten VLAN1-Geraets
.41.133" aus TP19 (es ist das coolify-Origin).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-16 19:28:50 +02:00

23 KiB
Raw Blame History

TP16.1 — Private Access via native Cloudflare Mesh (WARP Connector) auf dem CRS326 Core-Switch

Status 2026-08-16: 🟢 GATE OFFEN, ausführungsreif. 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: <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 · Setup Cloudflare Mesh using WireGuard · wgcf-connector (GitHub) · CF-Changelog „Introducing Cloudflare Mesh" · CF-Docs 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.

⚠️ Token-Permission-Gotcha (2026-07-17, real erlebt): 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 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.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 crs326-core nativ konfigurieren ✅ ausführungsreif (Werte finalisiert 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="<PrivateKey aus .conf>" 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 <<REDACTED>> 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: <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.