TP16.3 und TP14.4 als ausfuehrbare Runbooks
Beide TPs waren "Recherche durch / Umsetzung offen" bzw. "TODO" -- also jeweils eine Zeile, aus der eine frische Session nicht losarbeiten kann. TP16.3: der Schnitt (nautobot aus dem bf-nas-Ingress) ausformuliert, mit Vorbedingungen, PUT-Ablauf und Rollback. Zwei Dinge stehen jetzt explizit drin, die den TP sonst kippen: Mesh-Hostname-Routes sind Egress und veroeffentlichen nichts -- wer das verwechselt, baut den Public-Strang ab; und Nautobot ist nicht nur eine Web-UI, sondern die Deploy-Pipeline, also gehoert ein Job-Lauf zur Abnahme und nicht nur ein Browser-Aufruf. TP14.4: Ist-Stand nachgesehen -- in mcp/cloudflare/.env stehen weder CF_ACCESS_CLIENT_ID noch _SECRET, es ist also nichts halb erledigt. Das Secret gibt es nur im Moment der Rotation, das steht jetzt als Warnung davor. Abnahme braucht beide Faelle: mit Headern antwortet, ohne 403 -- sonst haette ein bypass-Hostname wie vw als Erfolg durchgehen koennen. Kein CF-Write, kein Token rotiert. Beides bleibt Papier. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -575,6 +575,11 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
|
||||
|
||||
### TP14.4 — Session D: CF-Access-Profil für Claude ▸ 🔜 TODO (nicht cutoff-kritisch)
|
||||
- CF-Token „claude code mcp" hat jetzt `Access: Service Tokens: Write`+`Read` → `claude-mcp` rotieren (`POST …/access/service_tokens/{id}/rotate`) → `CF_ACCESS_CLIENT_ID`/`_SECRET` in `mcp/cloudflare/.env` (chmod600). Memory `ref-cf-access-claude-profile`.
|
||||
- 📋 **Runbook: `docs/tp14.4-cf-access-token-runbook.md`** (2026-08-19). **Ist-Stand verifiziert:**
|
||||
`mcp/cloudflare/.env` enthält bisher weder `CF_ACCESS_CLIENT_ID` noch `_SECRET` — es ist nichts halb
|
||||
erledigt. 🔴 **Das Secret wird nur bei der Rotation ausgegeben**, wer es nicht sofort schreibt, muss
|
||||
erneut rotieren. Abnahme = ein Aufruf **mit** den Headern (antwortet) **und** einer **ohne** (403) —
|
||||
der Erfolgsfall allein beweist nichts, der Hostname könnte auf `bypass` stehen.
|
||||
|
||||
### TP14.5 — Session E: Freigabe-Gate → TP5-Firewall ▸ ✅ ERFÜLLT (2026-08-08) — kein eigener Change
|
||||
- Gate „TP14.1–14.3 durch (alle Origins/Connectoren in VLAN20)" ist erfüllt → **TP5 (Deny-by-default) ist entblockt.** Reines Reihenfolge-Gate, kein eigener Change.
|
||||
@@ -1429,6 +1434,15 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
|
||||
aus dem CF-Ingress nehmen (Kandidat: `nautobot` — nur Christoph nutzt es, im Mesh-Log mit 62 Treffern der am
|
||||
häufigsten per Mesh angefasste Dienst, und ein Fehlschlag kostet nichts Produktives), eine Woche fahren, dann
|
||||
entscheiden. **Rollback = eine Ingress-Zeile zurück.**
|
||||
- 📋 **Runbook: `docs/tp16.3-nautobot-mesh-only-runbook.md`** (2026-08-19) — der Schnitt oben, ausführbar
|
||||
ausformuliert: Vorbedingungen (Mesh-Route, Zugriff über `10.40.20.30:8080` **mit WARP von außerhalb**),
|
||||
Ingress-`PUT` über `CF_API_TOKEN` (**nicht** über den CF-MCP, der ist read-only), Rollback aus der
|
||||
gesicherten Config.
|
||||
- 🔴 **Der Abnahmepunkt, den man übersieht:** Nautobot ist nicht nur Web-UI, sondern die
|
||||
**Deploy-Pipeline**. Nach dem Schnitt gehört ein Job-Lauf zur Abnahme, nicht nur ein Browser-Aufruf
|
||||
— der Netmiko-Deploy kommt von `10.40.20.31` (`nautobot-worker`), nicht von `.20.30`.
|
||||
- ⚠️ Beim Kürzen des `ingress`-Arrays muss die **Catch-all-Regel die letzte bleiben**, sonst greift
|
||||
der Ingress für alle übrigen Hostnames nicht mehr.
|
||||
- **Abh.:** TP16.1 ✅ (Mesh live), TP16 (Public-Strang + dessen HA-Degradierung), **TP16.2** (der `vw`-Constraint
|
||||
liefert das Entscheidungskriterium). ⚠️ **Berührt TP5:** der Mesh-Verkehr braucht bei deny-by-default eine
|
||||
explizite `forward`-Zeile — s. TP5 Etappe 2.
|
||||
|
||||
70
docs/tp14.4-cf-access-token-runbook.md
Normal file
70
docs/tp14.4-cf-access-token-runbook.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# TP14.4 — CF-Access-Service-Token für Claude rotieren
|
||||
|
||||
> **Stand 2026-08-19 · vorbereitet, nicht ausgeführt.** Kurzes Runbook für einen Einmal-Schuss:
|
||||
> das Secret ist **genau einmal** sichtbar, danach nie wieder.
|
||||
|
||||
---
|
||||
|
||||
## 1. Ist-Stand (verifiziert 2026-08-19)
|
||||
|
||||
`mcp/cloudflare/.env` enthält **kein** `CF_ACCESS_CLIENT_ID` und **kein** `CF_ACCESS_CLIENT_SECRET`.
|
||||
Der TP steht also zu Recht offen — es ist nichts halb erledigt, was man erst zurückbauen müsste.
|
||||
|
||||
Vorhanden und nutzbar: `CF_API_TOKEN`, `CF_ACCOUNT_ID`, `CF_ZONE_ID` (chmod 600, gitignored).
|
||||
Das Token trägt laut TP14.4 bereits die nötigen Scopes **`Access: Service Tokens: Read + Write`**.
|
||||
|
||||
**Wozu das Ganze:** damit Claude Dienste hinter CF-Access erreichen kann, ohne den interaktiven
|
||||
Login-Flow — das ist das „komm schwer durch Tunnel"-Symptom aus Memory `ref_cf_access_claude_profile`.
|
||||
|
||||
---
|
||||
|
||||
## 2. Ablauf
|
||||
|
||||
1. **Token-ID ermitteln:**
|
||||
`GET /accounts/{account_id}/access/service_tokens` → den Eintrag `claude-mcp` heraussuchen, `id` notieren.
|
||||
2. **Rotieren:**
|
||||
`POST /accounts/{account_id}/access/service_tokens/{id}/rotate`
|
||||
3. 🔴 **Sofort schreiben.** Die Antwort enthält `client_id` **und** `client_secret` — das Secret wird
|
||||
**nur bei der Rotation** ausgegeben. Wer es nicht in diesem Moment wegschreibt, muss erneut rotieren
|
||||
(was den alten Wert ungültig macht, also nicht folgenlos ist).
|
||||
4. **Ablegen** in `mcp/cloudflare/.env`:
|
||||
```
|
||||
CF_ACCESS_CLIENT_ID=…
|
||||
CF_ACCESS_CLIENT_SECRET=…
|
||||
```
|
||||
Datei bleibt `chmod 600`; sie ist über `mcp/**/.env` bereits gitignored — **nicht** committen.
|
||||
5. **Konsolidieren** nach globaler Konvention: Werte zusätzlich in `~/.secrets/credentials.env`,
|
||||
Index-Zeile (Schlüssel → Herkunftspfad) in `~/.secrets/README.md`. Single Source of Truth bleibt
|
||||
der Originalort, `~/.secrets/` ist die Nachschlage-Kopie.
|
||||
|
||||
⚠️ **Beim Ausgeben von Fehlern/Tracebacks maskieren** — Projektregel aus
|
||||
`feedback_redact_router_passwords_in_tracebacks`, gilt hier genauso.
|
||||
|
||||
---
|
||||
|
||||
## 3. Abnahme
|
||||
|
||||
Ein Aufruf gegen einen **Access-geschützten** Hostname mit den beiden Headern:
|
||||
|
||||
```
|
||||
CF-Access-Client-Id: <client_id>
|
||||
CF-Access-Client-Secret: <client_secret>
|
||||
```
|
||||
|
||||
- **mit** Headern → der Dienst antwortet
|
||||
- **ohne** Header → `403`
|
||||
|
||||
Beides prüfen. Nur der Erfolgsfall allein beweist nichts (der Hostname könnte auf `bypass` stehen —
|
||||
`vw` tut das bewusst, s. TP16.2).
|
||||
|
||||
Danach Memory `ref_cf_access_claude_profile` nachziehen: dort steht bisher „Service-Token-Secret fehlt
|
||||
lokal" — das ist nach dieser Rotation nicht mehr wahr.
|
||||
|
||||
---
|
||||
|
||||
## 4. Fußnote mit Verfallsdatum
|
||||
|
||||
⏰ **Der `CF_API_TOKEN` selbst läuft am 2027-01-09 ab** (Memory `ref_cf_mcp_readonly_token_writes`).
|
||||
Er ist **account-owned**, verifiziert also über einen anderen Endpoint als ein User-Token — wer das
|
||||
verwechselt, hält ein funktionierendes Token für kaputt. Ohne ihn ist auch diese Rotation nicht mehr
|
||||
möglich, ebensowenig der TP16.3-Schnitt.
|
||||
121
docs/tp16.3-nautobot-mesh-only-runbook.md
Normal file
121
docs/tp16.3-nautobot-mesh-only-runbook.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user