TP16.1 ABGESCHLOSSEN: privater Zugang via natives WireGuard, ICMP repariert

4.5 Abnahme bestanden: 0% Ping-Loss auf alle BF-VLANs (vorher 100%), TCP mit
echtem Datenfluss ok, FQDN-Smoke beweist cloudflared-Public-Teil unberuehrt.
4.6 Auflage #4 erfuellt: Ist-Stand als intended config nach Nautobot,
65/68 -> Template ergaenzt -> 68/68 keine Drifts.

Zentrale Falle dokumentiert: /ip service address=10.40.0.0/16 sperrt das
Router-Mgmt aus der Mesh-Range 100.96.0.0/12 aus - Ping geht, SSH/REST/MikroMCP
sterben. Fuer branch/ap1/ap2 noch offen. Und: nc -z ueber WARP ist kein
Reachability-Beweis, der CF-Proxy terminiert den Handshake.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-17 12:28:03 +02:00
parent 44c1fbcca8
commit 63851ad0ad
3 changed files with 101 additions and 9 deletions

View File

@@ -1,13 +1,22 @@
# TP16.1 — Private Access via native Cloudflare Mesh (WARP Connector) auf dem CRS326 Core-Switch
> **🟡 Status 2026-08-17: §4.0–§4.4 DURCH, Mesh-Tunnel steht — Abnahme (§4.5) offen.** `cf-mesh` auf
> `crs326-core` handshaked mit dem CF-PoP, CF meldet den Node **healthy**, und die Route `10.40.0.0/16` zeigt
> bereits auf ihn statt auf `bf-pve`. **Zwei Dinge fehlen noch, beide erfordern dich:** (1) der Dashboard-only-
> Schalter „Allow all Cloudflare One traffic to reach enrolled devices" (§4.1 Schritt 2), (2) die Ping-Abnahme mit
> verbundenem WARP (§4.5). Danach ist die Auflage aus Ausnahme #4 fällig: Ist-Stand als intended config zurück
> nach Nautobot.
> # ✅ **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.**
>
> *Vorstufe:* **§4.0 DURCH — Plan-Gate offen, Mesh-Node existiert.** `POST /warp_connector` erfolgreich:
> **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.
>
> 🔴 **Einziger verbliebener Punkt, user-gated:** der Dashboard-only-Schalter „Allow all Cloudflare One traffic
> to reach enrolled devices" (§4.1 Schritt 2). **Der Mesh läuft nachweislich auch ohne ihn** — CF führt ihn als
> Prerequisite, für Device→Node/Subnetz war er offenkundig nicht nötig. Vermutlich zählt er erst für
> **Client↔Client** (Gerät zu Gerät). Setzen, wenn das gebraucht wird. `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.**
@@ -391,6 +400,66 @@ keine Templatezeilen — die gehören nach Nautobot und von dort gerendert.
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="<<REDACTED>>"` — 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.
<details><summary>Ursprüngliche Abnahme-Kriterien</summary>
### 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 %).
@@ -398,6 +467,8 @@ keine Templatezeilen — die gehören nach Nautobot und von dort gerendert.
(gitea) mehrfach → Beweis „keine Blindheit".
- FQDN-Smoke (Stichprobe gitea/hass/pve über `*.emsenhuber.net`) → der cloudflared-Public-Teil (3-Tunnel-HA) blieb unberührt.
</details>
## 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.