187 lines
9.9 KiB
Markdown
187 lines
9.9 KiB
Markdown
# TP16.3 — Probe: `nautobot` aus dem CF-Ingress nehmen (mesh-only)
|
||
|
||
> **Stand 2026-08-19 · vorbereitet, nicht ausgeführt.** Die Recherche zu TP16.3 ist seit 2026-08-17
|
||
> durch; offen war nur die Umsetzung. Dieses Runbook macht daraus einen ausführbaren, in einer Minute
|
||
> zurücknehmbaren Schnitt.
|
||
|
||
---
|
||
|
||
## 1. Worum es geht (in drei Sätzen)
|
||
|
||
Der private Datenpfad läuft seit TP16.1 über **natives WireGuard auf dem Core** (`cf-mesh`), nicht über
|
||
Container — belegt, nicht vermutet: im TP5-Log-Lauf standen **137 Zeilen `in:cf-mesh`**. Für Dienste,
|
||
die **niemand ohne WARP im Browser** aufrufen muss, ist `cloudflared` damit überflüssiger Aufwand:
|
||
eine Ingress-Zeile, ein Connector-Paar, eine HA-Frage. Diese Probe nimmt **einen** solchen Dienst
|
||
heraus und schaut eine Woche zu.
|
||
|
||
### Warum ausgerechnet `nautobot`
|
||
|
||
| Kriterium | `nautobot` |
|
||
|---|---|
|
||
| Wer nutzt es | nur Christoph — kein Fremdgerät, kein Gast |
|
||
| Braucht es einen Client ohne Access-Login? | **nein** (das ist das TP16.2-Kriterium, an dem `vw` hängt) |
|
||
| Wie oft schon per Mesh angefasst | **62×** im TP5-Log — der Spitzenreiter |
|
||
| Was kostet ein Fehlschlag | nichts Produktives; im Zweifel greift man per IP zu |
|
||
|
||
---
|
||
|
||
## 2. 🔴 Die Verwechslungsfalle, die den ganzen TP kippen kann
|
||
|
||
**Mesh-Hostname-Routes sind Egress, nicht Publishing.** Cloudflare kennt seit 2026-07-02 auch
|
||
Hostname-Routes auf dem Mesh — die klingen so, als könnten sie den Ingress ersetzen. Sie können es
|
||
nicht: sie leiten *ausgehenden* Verkehr zu diesem Hostname über den Node und egressen mit dessen
|
||
öffentlicher IP. **Sie veröffentlichen nichts.**
|
||
|
||
Wer das verwechselt, baut den öffentlichen Strang ab und wundert sich, warum von außen nichts mehr geht.
|
||
→ **Diese Probe ersetzt nichts. Sie entfernt einen Dienst, der öffentlich gar nicht gebraucht wird.**
|
||
|
||
---
|
||
|
||
## 3. Vorbedingung — VOR dem Schnitt prüfen (read-only)
|
||
|
||
Alle drei Punkte müssen erfüllt sein, sonst wird nicht geschnitten:
|
||
|
||
1. **Die Mesh-Route steht.** Auf dem Core: die vnet-Route `10.40.0.0/16` zeigt auf `bf-core-mesh`
|
||
(TP16.1 §4.4, „Weg A"). Ohne sie ist Mesh-Zugriff auf `10.40.20.30` gar nicht möglich.
|
||
2. **Der Dienst antwortet über die private IP, mit aktivem WARP** — von einem Client, der *nicht* im
|
||
BF-LAN steht (das ist der einzige Test, der etwas beweist; im LAN geht es ohnehin):
|
||
```
|
||
http://10.40.20.30:8080
|
||
```
|
||
3. ⚠️ **ICMP nicht als Beweis nehmen, aber auch nicht als Gegenbeweis.** Der Mesh kann ICMP (das war
|
||
der Gewinn von TP16.1). Wenn Ping scheitert, TCP aber geht, ist zuerst zu prüfen, ob lokal noch ein
|
||
WARP-Client läuft — s. Memory `ref_warp_local_icmp_trap`. Umgekehrt ist ein erfolgreicher Ping kein
|
||
Beleg, dass der HTTP-Dienst erreichbar ist.
|
||
|
||
---
|
||
|
||
## 4. Der Schnitt
|
||
|
||
**Was:** die `nautobot`-Zeile aus der Ingress-Config des Tunnels **`bf-nas`** entfernen.
|
||
Die DNS-CNAME-Zeile bleibt zunächst stehen (Rollback bleibt so eine reine Ingress-Operation).
|
||
|
||
**Womit:** die Cloudflare-API mit `CF_API_TOKEN`.
|
||
|
||
> 🔴 **Nicht über den Cloudflare-MCP.** Der ist **strikt read-only** — Writes gehen ausschließlich über
|
||
> die API mit dem Token (Memory `ref_cf_mcp_readonly_token_writes`). Das Token ist **account-owned**,
|
||
> nutzt also einen anderen verify-Endpoint als ein User-Token, und **läuft am 2027-01-09 ab**.
|
||
|
||
**Wo die Werte stehen:** `mcp/cloudflare/.env` (chmod 600, gitignored) —
|
||
`CF_API_TOKEN`, `CF_ACCOUNT_ID`, `CF_BF_TUNNEL_ID`.
|
||
|
||
**Ablauf:**
|
||
|
||
1. **Ist-Config sichern** (das ist der Rollback):
|
||
`GET /accounts/{account_id}/cfd_tunnel/{tunnel_id}/configurations` → JSON wegschreiben, mit Datum.
|
||
2. Aus dem `ingress`-Array die `nautobot`-Zeile entfernen. ⚠️ Die abschließende Catch-all-Regel
|
||
(`service: http_status:404` ohne `hostname`) **muss die letzte bleiben** — sonst greift der Ingress
|
||
für alle übrigen Hostnames nicht mehr.
|
||
3. **Zurückschreiben:** `PUT` derselben Route mit dem gekürzten Array.
|
||
4. **Gegenprobe am Gerät, nicht am Statuscode:** ein `GET` der Config liest zurück, was wirklich steht.
|
||
*(Dieselbe Regel wie in TP22/TP23: ein „success" der API ist kein Wirksamkeitsnachweis.)*
|
||
|
||
---
|
||
|
||
## 5. Abnahme
|
||
|
||
| Prüfung | Erwartung |
|
||
|---|---|
|
||
| Öffentlicher FQDN `nautobot.…` ohne WARP | **nicht mehr erreichbar** (das ist der Zweck) |
|
||
| `http://10.40.20.30:8080` mit WARP, von außerhalb des LAN | erreichbar |
|
||
| Die übrigen Hostnames auf `bf-nas` (gitea, grafana, pve-mcp, vw, ds-bf) | **unverändert erreichbar** |
|
||
| Golden-Config-Deploys (Netmiko aus Nautobot) | laufen weiter |
|
||
|
||
🔴 **Der letzte Punkt ist der, den man übersieht:** Nautobot ist nicht nur eine Web-UI, sondern die
|
||
Deploy-Pipeline. Der Netmiko-Deploy kommt von **`10.40.20.31`** (`nautobot-worker`), nicht von `.20.30`
|
||
— das war einer der vier TP5-Funde. Der Schnitt betrifft nur den *eingehenden* öffentlichen Weg, aber
|
||
nach dem Schnitt gehört ein Job-Lauf zur Abnahme, nicht nur ein Browser-Aufruf.
|
||
|
||
---
|
||
|
||
## 6. Betriebsfenster und Rollback
|
||
|
||
- **Eine Woche fahren**, dann entscheiden: bleibt es so, oder kommt es zurück.
|
||
- **Rollback = die gesicherte Ingress-Config aus Schritt 1 zurückschreiben.** Eine Zeile, eine Minute.
|
||
- Danach ist zu entscheiden, ob weitere Dienste der rechten Spalte folgen (`pve`, `portainer`,
|
||
`grafana`, `pve-mcp`, `ha-mcp`, `gitea-mcp`, `ds-bf`, `ccu`, `iobroker`, `z2m25`).
|
||
|
||
### Was in dieser Probe ausdrücklich NICHT entschieden wird
|
||
|
||
- **`vw`, `hass`, `n8n` bleiben öffentlich** — harte Kriterien: Bitwarden-Clients (TP16.2-Constraint),
|
||
die HA-Companion-App (das 400-Muster), eingehende Webhooks von Dritten.
|
||
- **`gitea` ist offen** und wird hier nicht mitentschieden: Git-Clients/CI von außen können keinen
|
||
interaktiven Access-Login. Braucht eine eigene Prüfung, ob es solche Zugriffe real gibt.
|
||
|
||
---
|
||
|
||
## 7. Nebeneffekt, der den Fall stärkt
|
||
|
||
Die **TP16-HA-Degradierung** (`bf-nas` und `bf-pve` haben je nur **einen** Connector, Befund 2026-08-16)
|
||
verliert an Gewicht, je weniger Dienste an cloudflared hängen. Das ersetzt die Reparatur nicht —
|
||
aber es verkleinert, was der SPOF trifft.
|
||
|
||
---
|
||
|
||
## 8. ▶️ AUSGEFÜHRT 2026-08-24 — der Schnitt sitzt, Probe läuft
|
||
|
||
**Vorbedingungen (§3), alle erfüllt:**
|
||
|
||
| Prüfung | Ergebnis |
|
||
|---|---|
|
||
| vnet-Route `10.40.0.0/16` → `bf-core-mesh` | ✅ vorhanden (CF `teamnet/routes`) |
|
||
| `cf-mesh` am Core | ✅ `R`, last-handshake 33 s, 198 MiB rx / 738 MiB tx |
|
||
| `http://10.40.20.30:8080` **von außerhalb des LAN, WARP aktiv** | ✅ HTTP 302 in 0,18–0,36 s |
|
||
| ICMP zu `10.40.20.30` | ✅ 0 % Loss *(kein Beweis, aber es schließt die WARP-Falle aus)* |
|
||
|
||
🎯 **Der Testclient war zufällig genau der richtige:** die Session lief auf einem Host im
|
||
**Hotspot-Netz `172.20.10.13`** mit aktivem WARP — also *nicht* im BF-LAN. Das ist der einzige Aufbau,
|
||
der für Vorbedingung 2 etwas beweist; aus dem LAN heraus geht es ohnehin.
|
||
|
||
**Der Schnitt:** `nautobot.emsenhuber.net` aus dem Ingress von `bf-nas`
|
||
(`220c9958-401b-4111-9451-f3db09bba2d5`) entfernt, **8 → 7 Regeln**, Catch-all weiterhin letzte.
|
||
Rollback-Sicherung: **`docs/cf-backup/bf-nas-ingress-20260824-vor-tp16.3.json`**.
|
||
Gegenprobe **durch Zurücklesen der Config vom Dienst**, nicht am Statuscode: `nautobot noch drin? False`.
|
||
|
||
**Abnahme (§5):**
|
||
|
||
| Prüfung | Ergebnis |
|
||
|---|---|
|
||
| `10.40.20.30:8080` mit WARP von außerhalb | ✅ 302 in 0,16–0,17 s |
|
||
| `gitea` / `grafana` / `pve-mcp` / `vw` / `ds-bf` | ✅ je 3 Läufe, unverändert (200/302/302/200/302) |
|
||
| **Golden-Config-Pipeline** | ✅ Backup → Intended → Compliance, **104/104 compliant, 0 Drifts** |
|
||
| Öffentlicher FQDN nicht mehr bedient | ⚠️ **so nicht testbar — s.u.** |
|
||
|
||
### 🔴 Befund: die erste Abnahmezeile aus §5 ist als Test untauglich
|
||
`https://nautobot.emsenhuber.net/` antwortet **vor und nach** dem Schnitt mit **HTTP 302** — und zwar
|
||
identisch, weil der 302 **nicht vom Origin kommt, sondern von Cloudflare Access**:
|
||
`location: https://emsenhuber.cloudflareaccess.com/cdn-cgi/access/login/nautobot.emsenhuber.net`.
|
||
Access greift **vor** dem Tunnel; der Request erreicht den Ingress gar nicht, solange keine
|
||
Access-Session besteht. Im mitgelieferten JWT steht `"auth_status":"NONE"` bei gleichzeitig
|
||
`"is_warp":true` — **WARP-Identität allein erfüllt die Policy für `nautobot` nicht**, es braucht den
|
||
interaktiven Login.
|
||
|
||
⇒ **Konsequenzen, beide wichtig:**
|
||
1. **Der Statuscode des öffentlichen FQDN kann diesen Schnitt weder belegen noch widerlegen.** Der
|
||
gültige Nachweis ist das **Zurücklesen der Ingress-Config** (oben) — dieselbe Regel wie in
|
||
TP22/TP23: nicht die Antwort des Frontends zählt, sondern der Zustand am Dienst.
|
||
2. **Der öffentliche Weg war schon vorher zu.** Nautobot war nie ohne Access-Login erreichbar; der
|
||
Schnitt entfernt also die *zweite* von zwei Türen, nicht die erste. Das schmälert den Gewinn nicht
|
||
(eine Ingress-Zeile und ein Connector-Paar weniger), aber es korrigiert die Erwartung: **hier wurde
|
||
keine offene Tür geschlossen, sondern eine überflüssige entfernt.**
|
||
|
||
📌 **Nebenprüfung, die den Schnitt hätte kippen können:** `nautobot/gc-configplan.py` spricht
|
||
`http://10.40.20.30:8080/api` — die **private** IP. Kein Repo-Skript benutzt den öffentlichen Namen
|
||
(`grep` über `*.py`/`*.sh`/`*.yml`/`*.env`: kein Treffer). Die Werkzeugkette hängt also nicht am
|
||
gekappten Weg. **Das gehört vor jeden weiteren Schnitt der rechten Spalte geprüft.**
|
||
|
||
### ⏳ Offen: das Betriebsfenster
|
||
**Eine Woche fahren (bis ~2026-08-31), dann entscheiden.** Rollback = die gesicherte Config
|
||
zurückschreiben, eine Minute. Danach steht die Frage, ob `pve`, `portainer`, `grafana`, `pve-mcp`,
|
||
`ha-mcp`, `gitea-mcp`, `ds-bf`, `ccu`, `iobroker`, `z2m25` folgen.
|
||
|
||
### 📌 §7 ist überholt
|
||
Der dort beschriebene Nebeneffekt („die HA-Degradierung verliert an Gewicht") ist gegenstandslos:
|
||
**TP16 ist am selben Tag repariert** — alle drei bf-Tunnel laufen wieder auf 2 Connectoren
|
||
(`cf-tunnel-mapping.md §15`). Der Fall für TP16.3 steht damit auf dem verbliebenen Argument, und das
|
||
ist das eigentliche: **weniger bewegliche Teile für Dienste, die öffentlich nicht gebraucht werden.**
|