# Handoff 2026-09-01 — TP41 CF-Access-Reparatur + Zero-Trust-Konsolidierung > **Branch `tp41-cf-access-repair`.** Alle CF-Änderungen sind **live via API** gemacht (Schreibpfad > `CF_API_TOKEN` + curl, cf-tunnel-mapping.md §11). Working-Tree: `docs/cf-backup/tp41-20260901/` > (Snapshots, secret-frei) + dieser Handoff + `teilprojekte.md`-Eintrag; `mcp/cloudflare/.env` > (gitignored) wurde geändert (Token-Konsolidierung). Helper-Skripte lagen im Scratchpad/`/tmp` > (`cf_session.py`, `cf_harden.py`, `cf_groups.py`, `cf_vwadmin.py`, `cf_carveout.py`, `t4a.py`). ## Auslöser Zugriff auf CF-geschützte Apps zwang Christoph **und Geli** faktisch **täglich** zum E-Mail-OTP-Login, obwohl 30 Tage gelten sollten; föderierte Logins (GitHub/Google) „funktionieren nicht". Root-Causes: (1) **Team-Domain-Rename `bf4` → `emsenhuber`** ⇒ Provider-OAuth-Apps kennen die neue Callback-URL nicht; (2) App-**Session 24h**; (3) diverse Ordnungs-/Sicherheitslecks. Daraus wurde eine Zero-Trust-Konsolidierungs-Runde. ## ✅ LIVE erledigt (verifiziert) - **Session 720h** auf Wildcard (`16aaec78`), vw-App (`1dbc45ca`), Warp-Login (`6c32fe23`). - **login_method-Loch geschlossen:** reusable Policy **`bf-users` (`4ccc9b7e`)** = `allow` mit **nur** `email:christoph@emsenhuber.com` + `email:@outlook.de` (kein `login_method`-Include mehr). Attached an Wildcard / App-Launcher (`6df5c431`) / Warp-Login. Reusable Policy `8e44f5d1` **entkoppelt** → hängt jetzt **nur noch an Terpenfinder** (Fremdprojekt, nicht angefasst). - **Access-Gruppen (Prefix `zt-`):** `zt-humans` (`6eeec7f3`, christoph+geli), `zt-home` (`c0f8ba0d`, IP `90.146.33.177/32`), `zt-automation` (`1b60b608`, svc-token `65f1821a`). Policies `bf-users` / `bf4` (`5165d139`) / `claude-mcp` (`39fe0e9d`) zeigen jetzt **auf die Gruppen** → Personen-/IP-/Token-Pflege künftig nur noch in der Gruppe. - **vw = voll Bypass** (Passwort-Lebensader: Web-Vault + Bitwarden-App/Extension/Desktop offen, **kein** CF-Login davor). Ein Carve-out-Versuch (nur Client-Pfade bypass, Rest gaten) wurde **verworfen**: CF-**Bypass kurzschließt** spezifischere path-Apps, und `/` (Web-Vault-Root) teilt den Prefix mit `/admin` — nicht trennbar. Die 3 Experiment-Apps wurden wieder gelöscht. - **`vw.emsenhuber.net/admin` öffentlich geblockt** per **WAF-Custom-Rule** (Zone-Ruleset `416af72d0dd54853805875bf5752afd7`, Regel `f26744eb…`, `action=block`, expr `http.host eq "vw.emsenhuber.net" and (path eq "/admin" or starts_with(path,"/admin/"))`). **Verifiziert über CF-Edge** und vom User per **Mobilfunk** („you have been blocked"). Lokal (Heim-LAN, Split-Horizon → Caddy) und später WARP-intern bleibt `/admin` erreichbar. - **API-Token konsolidiert:** `mcp/cloudflare/.env` `CF_API_TOKEN` = **`…4148`** (id `493ed8bd…`), Backup `mcp/cloudflare/.env.bak-tp41-20260901`. Der User hat `…4148` anschließend **vollständig** gemacht (u.a. **Access: Service Tokens Write** + **Zero Trust Write** + WAF + Groups + DNS für emsenhuber.net **und** teilzeitladen.xyz). **Altes Token `…2174` (id `9984078812bc18d4ebb6a64b570144f2`) ist am 2026-09-01 vom User revoked ✅.** > ⚠️ **API-Token-Altlast → ARBEITSPAKET für die Folgesession (User 2026-09-01):** der User fand ein > weiteres API-Token **`b00722522c685920f73cfcc62e9555d9`** mit **exakt denselben Permissions wie > `d47435d5c86b764bbf7c5b19ae827033`** — vermutlich eine **blaueblüten/Terpene-Altlast**. > **Auftrag: Projekte nachziehen** — feststellen, welcher Consumer welches der beiden Tokens nutzt, > auf **eines** konsolidieren, das verwaiste dann abbauen. Token-/Credentials-Hygiene (TP30-nah). ## 🔜 OFFEN (Token reicht jetzt für alles außer T5) - **T4 — kumulatives Service-Token `zt-svc`** (Basis: `t4a.py`, war nur an fehlender Perm gescheitert): 1. `claude-mcp` (`65f1821a`) **rename → `zt-svc`** + **rotate** (Secret einmalig). 2. `terpene-svc`-Policy (`bf7d2e5e`) **additiv** += group `zt-automation` (akzeptiert dann alt **und** neu). 3. Secret ablegen: `mcp/cloudflare/.env` (`CF_ACCESS_CLIENT_ID/SECRET`) **und** `~/.secrets/credentials.env` — dort auch die Terpene-Aliase **`TERPENE_ACCESS_CLIENT_ID/SECRET`** (die die Tools lesen) **und** `TERPENE_VERIFY_CF_CLIENT_ID/SECRET` (beide zeigten auf terpene-verify-v2). 4. **Verify Terpene:** `python3 ~/dev/blaueblueten-n8n/tools/test_whoami.py` (→ `420.teilzeitladen.xyz/api/admin/whoami`, muss weiter funktionieren). 5. Erst **nach** grünem Verify: `terpene-verify-v2` aus `terpene-svc` entfernen, dann **`terpene-verify` (`a6be7efa`) + `terpene-verify-v2` (`1082bad1`) löschen**. 6. `~/.secrets/README.md`-Index nachziehen. opencode/`exports.zsh` optional (gui-env-sync). → End: **ein** Service-Token `zt-svc` über Gruppe `zt-automation` in allen Apps + Consumern. - **T3 — WARP löst `emsenhuber.net` intern auf** (User-Wahl „Variante 2"): WARP-Device-Profile `fallback_domains` += `emsenhuber.net` → interner Resolver **`10.40.20.1`** (liefert Split-Horizon `*.emsenhuber.net` → Caddy `10.40.20.40`). Route **`10.40.0.0/16` existiert** (tunnel `d1da7c8a`). Danach `/admin` + alle internen Namen über **WARP** via Caddy erreichbar (kein CF/WAF). ⚠️ **account-/ profilweit** → als Lifecycle-Change **proaktiv melden**. (Token hat jetzt Zero-Trust-Write.) - **T2 — 404 statt 403 (User-Wunsch):** WAF-**Custom-Response ist Free-Plan-gesperrt** („not entitled to use a custom response"). 403-Block bleibt — außer man macht es **Origin-seitig in Caddy** (`respond 404` wenn Header `CF-Connecting-IP` gesetzt = via CF gekommen; lokal/WARP-intern ohne Header → durchlassen). Das komponiert sauber mit T3 (WARP-intern trifft Caddy ohne CF-Header). Eigener Caddy-Deploy (LXC 130). - **T5 — IdP-Konsolen (USER-Hausaufgabe, DER Daily-OTP-Fix, bestätigt kaputt):** User-Test lieferte GitHub **„The redirect_uri is not associated with this application"**. Fix in den **Provider-Konsolen**: - **GitHub** OAuth-App `Ov23li2h96hAkGRPJXYV` → *Authorization callback URL* - **Google** OAuth-Client → *Authorized redirect URIs* - beide = **`https://emsenhuber.cloudflareaccess.com/cdn-cgi/access/callback`**. Danach Login-Test je Provider; Gegenprobe CF-Audit `GET /accounts/…/access/logs/access_requests` (`identity_provider=github/google` statt `onetimepin`). - **T6 (optional) — Rate-Limit:** vw `/identity/connect/token` + gitea-Login. **Kein** Managed-Challenge vor vw-API/`/identity`, git-/api-Pfaden, `assetlinks.json` (bricht Bitwarden-Clients / git / MCP). - **T7 — Doku:** ggf. `docs/tp41-cf-access-repair-runbook.md` (Detail-Runbook) ergänzen. ## 🆕 NEU — dw/Geli an der Auth vorbei (Terpenfinder, CGNAT) Ziel: dw erreicht `420.teilzeitladen.xyz` **ohne** CF-Login. IP-Bypass wie `bf4` scheitert an Gelis **CGNAT**. **Eleganteste Lösung (User-Idee): Split-Horizon am dw-Standort** — dw-FritzBox setzt lokalen DNS-Override `420.teilzeitladen.xyz` → BF-Caddy (`caddy-bf1` / `10.40.20.40`), erreichbar über den bestehenden **WG-Tunnel dw↔core**; Caddy braucht einen Block `420.teilzeitladen.xyz` → macmini-Worker (`10.40.20.167:8890`) **mit Cert** (DNS-01; Token hat jetzt DNS-Write für `teilzeitladen.xyz`). Vorab prüfen: (a) FritzBox-Host-Override möglich; (b) WG-Route dw→10.40.20.40; (c) Worker wirklich auf `macmini:8890` von Caddy erreichbar. **Cross-Projekt** (teilzeitladen.xyz). Alternative: Geli-Gerät in **WARP** (Session statt Login, CGNAT unkritisch) + Access-Policy auf WARP-Identität. ## ❓ Scope-Klärung für die neue Session Der User nannte „TP3/TP4/TP5 gleich mitnehmen" — **diese sind laut `teilprojekte.md` abgeschlossen** (TP3 WLAN-Rebuild ✅, TP4 ap1-Router/WAN-Edge ✅, TP5 deny-by-default ✅). Vermutlich Zahlendreher. **Offene Kandidaten:** TP6 (CAKE, Projektziel), TP16.1 (WARP-Mesh nativ), TP16.3, TP24 (DC-USV), TP25 (WLAN-Signal/Re-Walk). **Vor Start klären, welche gemeint sind** (je eigene Session empfohlen; Subagenten nur für parallele, abgegrenzte Teilaufgaben innerhalb einer Session). ## 🧭 Test-Vantage-Lehre (wichtig, hat hier Stunden gekostet) Von der **Heim-IP** löst `*.emsenhuber.net` → `10.40.20.40` (Caddy) und **umgeht CF komplett** — CF-Access/ WAF-Gating ist von zuhause **nicht** sichtbar (alles wirkt „offen"). Edge-Verhalten **erzwingen** mit `curl --resolve :443:` (CF-IP via `dig @1.1.1.1 +short `). **WebFetch ist für Access/Redirects unzuverlässig** (erreichte Origin trotz Gating) — **nicht** als Beweis verwenden; echter Remote-Test = User-Gerät im Mobilfunk. ## Referenz-IDs Apps: Wildcard `16aaec78` · vw/hass/gitea-Bundle `1dbc45ca` · App-Launcher `6df5c431` · Warp-Login `6c32fe23` · Terpenfinder v3 `e6a7991f`. Policies: `bf-users` `4ccc9b7e` · `bf4` `5165d139` · `claude-mcp` `39fe0e9d` · `terpene-svc` `bf7d2e5e` · (Terpene-only) `8e44f5d1`. Gruppen: `zt-humans` `6eeec7f3` · `zt-home` `c0f8ba0d` · `zt-automation` `1b60b608`. Service-Tokens: `claude-mcp`→`zt-svc` `65f1821a` · `terpene-verify` `a6be7efa` (Waise) · `terpene-verify-v2` `1082bad1`. WAF: Ruleset `416af72d…`, Regel `f26744eb…`. Team-Domain `emsenhuber.cloudflareaccess.com` (Org-`name`-Altlabel `bf4…` nicht anfassen). --- ## ✅ Update — Folgesession 2026-09-01 (T4 durch, Token-Altlast geklärt, Drift-Korrekturen) ### T4 — zt-svc ✅ DURCH (Variante B, inkl. Worker) - `claude-mcp` (`65f1821a`) → **`zt-svc`** umbenannt **und rotiert** (Secret in `mcp/cloudflare/.env` `CF_ACCESS_CLIENT_ID/SECRET` + `~/.secrets/credentials.env`; dort `TERPENE_ACCESS_*` und `TERPENE_VERIFY_CF_*` jetzt = zt-svc). client_id `ad1b47dee8ba9625eebb30247d163dda.access`. - **Worker-Pin gefunden** (`blaueblueten-n8n/worker/src/index.js:44`): Admin wird nur dem Token mit `common_name == env.TERPENE_SVC_CLIENT_ID` gewährt. Deshalb Variante B: **`wrangler secret put TERPENE_SVC_CLIENT_ID`** = zt-svc-client_id (wrangler-OAuth-Session, `workers_scripts:write` — die „Deploy blockiert"-Notiz galt nur für API-Tokens). Kein Code-Redeploy nötig. - `terpene-svc` (`bf7d2e5e`) include = **nur noch Gruppe `zt-automation`** (`1b60b608`). **`terpene-verify` (`a6be7efa`) + `terpene-verify-v2` (`1082bad1`) gelöscht.** Einziges verbleibendes Access-Service-Token = **`zt-svc`**. Verify: `test_whoami.py` → `admin:true`, common_name = zt-svc. ✅ ### Token-Altlast (b00722 / d47435) — GEKLÄRT (Rollen waren im Ur-Handoff vertauscht) - **`b00722`** ist **NICHT** die Altlast, sondern das **LIVE** von Caddy genutzte DNS-01-Token (LXC130 `/etc/caddy/caddy.env`, SSoT). Certs valide bis Nov 2026. - **`d47435`** = **stale Klartext-Kopie** in `credentials.env` (gleiche Perms, kein Live-Consumer). n8n hält **0 Credentials**, blaueblüten hält keinen der beiden. `credentials.env`-Kopie am 2026-09-01 auf **b00722** realignt (SSoT-Abgleich). - 🔜 **USER-Aktion:** **`d47435` revoken** (Dashboard oder `…4148` kurz „Account API Tokens Write" geben — `…4148` kann API-Tokens weder listen noch revoken, Bestätigung 9109/1000). ### Drift-Korrekturen (live gemessen) - **T3-Resolver:** interner Split-Horizon liegt auf **`10.40.20.36`/`.37`** (→ `*.emsenhuber.net = .40`), **nicht** auf `10.40.20.1` (der liefert public CF). T3-fallback_domains müssen auf `.36/.37` zeigen. - **`10.40.20.16/.17`** (AdGuard laut CLAUDE.md) sind **stale** → neu als **TP42** angelegt (`docs/handoff-20260901-tp42-naming.md`): `.bf.lan`/`.bf.emsenhuber.net`-Vereinheitlichung. ### 🔴 Leak (Transparenz) Beim maskierten Lesen von `credentials.env` ist der Wert **`TERPENE_ANDROID_KEYSTORE_PW`** ins Sitzungstranskript geraten (Regex-Loch `_PW`). **Rotation erwägen** (Terpene-Android-Signing). ### T3 — WARP-intern ✅ APPLIED (2026-09-01) - Ziel-Profil geklärt: das custom „Cloudflare Mesh"-Profil matcht **nur** `warp_connector@…` → Christoph/Geli laufen im **Default-Profil** (`0197d1b3`). `fallback_domains += emsenhuber.net → [10.40.20.36, 10.40.20.37]` dort gesetzt (15 Suffixe). Tunnel-Route `10.40.0.0/16` via `d1da7c8a` existiert; Exclude-Liste enthält **kein** `10.0.0.0/8` (nur `192.168/16` + CGNAT-minus-Mesh) → `.36/.37` sind über WARP routbar. - ⏳ **User-Verify:** WARP am Gerät **neu verbinden** (Profil-Cache), dann `dig vw.emsenhuber.net → .40` und `/admin` über WARP erreichbar. ### T2 — 404 statt 403 ✅ DURCH (2026-09-01) - Caddyfile `vw`-Block: `@cf_admin {header CF-Connecting-IP *; path /admin /admin/*} → respond 404`. Deploy LXC130 (Backup `/etc/caddy/Caddyfile.bak-tp41-20260901`), reload ok. - WAF-Regel **`f26744eb` gelöscht** (nur noch die gitea-skip-Regel `3c707c3c` im Ruleset). - Verify: **Edge `/admin` = 404** (war 403), Edge `/` = 200, **lokal/WARP `/admin` = 200**. ✅ ### Noch offen - **d47435-Revoke** (User, Account API Tokens Write). **T3-Client-Verify** (User, WARP neu verbinden). - **T5** (GitHub/Google-Callbacks — der eigentliche OTP-Fix, User) — **Schritt-für-Schritt:** [`docs/tp41-t5-idp-callback-anleitung.md`](tp41-t5-idp-callback-anleitung.md). **T6/T7/dw-Geli**. - 🔴 **`TERPENE_ANDROID_KEYSTORE_PW`-Rotation erwägen** (Transkript-Leak, s.o.).