TP16.3 §1: die drei Blocker remote behoben (Ingress, Firewall via GC, ap3-Cred)

A) bf-public-Ingress von http://10.40.20.40:80 auf https://10.40.20.40:443
   umgestellt, originServerName je Regel. Die :80-Variante haette ueber
   Cloudflare eine Redirect-Schleife gebaut (Caddy antwortet dort 308 auf
   https, das Ziel laeuft zurueck in denselben Tunnel).

B) Freigabe vlan99 -> vlan20:443 auf crs326-core angelegt - ueber Nautobot
   Golden Config, NICHT per Direkt-Write: ip-firewall-filter steht seit TP26
   unter Drift-Kontrolle, und die zwei genehmigten Ausnahmen (#2 Bring-up,
   #4 WireGuard-Keys) decken diesen Fall nicht.
   - Backup vorher: core-pre-tp16.3-blockerB-20260831.backup (TP22-Regel 3)
   - gc-manual-plan.py, Change-ID TP16.3-BlockerB, Plan Completed
   - Position verifiziert: direkt vor TP5: log-only / TP5: deny-by-default
   - Re-Baseline ingest-core-firewall.py --apply, dann --refresh-compliance
     => 135/135 compliant, 0 Drifts
   - funktional: /tool fetch von ap3 erreicht Caddy und bekommt einen
     TLS-Alert VON Caddy - ein Alert kann nur vom Server kommen, das Paket
     ist also da. Der Alert ist die SNI-Abweisung (ohne SNI 000, mit
     korrektem SNI 200) und damit genau der Grund fuer originServerName.

C) ROUTER_AP3_USER/_PASS in ~/.secrets/credentials.env nachgetragen (war nur
   in ~/.claude.json), chmod 600, Sicherung .bak-20260831. REST-Auth gegen
   ap3 = HTTP 200.

Vor Ort startet die Arbeit damit bei §2/§3 (container.npk, device-mode,
Container). Quick-Index, TP16.3-Abschnitt und Ist-Stand mitgezogen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-31 08:06:38 +02:00
parent 810126c01b
commit badf715f5a
2 changed files with 44 additions and 15 deletions

View File

@@ -40,6 +40,11 @@ liest, liest ihn als Erstes: er wird bei jeder Änderung mitgezogen, nicht nur d
unterstellte einen Registry-Pull (`fetch: no` auf ap1/ap2) und enthielt eine **falsche** unterstellte einen Registry-Pull (`fetch: no` auf ap1/ap2) und enthielt eine **falsche**
Firewall-Freigabe-Behauptung. **CF-Stand:** `bf-public` `inactive` / 0 Connectoren; die vier Firewall-Freigabe-Behauptung. **CF-Stand:** `bf-public` `inactive` / 0 Connectoren; die vier
öffentlichen Namen laufen noch über bf-mm/bf-nas/bf-pve **an Caddy vorbei** direkt aufs Backend. öffentlichen Namen laufen noch über bf-mm/bf-nas/bf-pve **an Caddy vorbei** direkt aufs Backend.
- 🆕 **TP16.3 §1-Blocker remote erledigt (2026-08-31):** Ingress von `bf-public` auf
`https://10.40.20.40:443` + `originServerName`; Firewall-Freigabe `vlan99 → vlan20:443` **über
Nautobot GC** (Plan `Completed`, Re-Baseline `ingest-core-firewall.py --apply`, danach
**135/135 compliant / 0 Drifts**); `ROUTER_AP3_PASS` in `~/.secrets`. **Offen bleibt nur die
Vor-Ort-Session** (§2/§3: `container.npk`, `device-mode`, Container) und danach Flip + Abbau.
- 🆕 **Globale Verhaltensregeln konsolidiert (2026-08-29, TP35 Schicht 3):** `~/.config/opencode/AGENTS.md` ist jetzt die **SSoT** für globale Verhaltensregeln (Secrets, VLAN-Schema, 5 Lessons, SoC, Übergabe-Mechanik, Mid-Action-Checkpoint, Commit-Hinweis, Backup-Guard). `~/.claude/CLAUDE.md` hält denselben Block als Claude-Code-Spiegelung mit Verweis auf die SSoT. opencode liest AGENTS.md mit Vorrang vor `~/.claude/CLAUDE.md`. - 🆕 **Globale Verhaltensregeln konsolidiert (2026-08-29, TP35 Schicht 3):** `~/.config/opencode/AGENTS.md` ist jetzt die **SSoT** für globale Verhaltensregeln (Secrets, VLAN-Schema, 5 Lessons, SoC, Übergabe-Mechanik, Mid-Action-Checkpoint, Commit-Hinweis, Backup-Guard). `~/.claude/CLAUDE.md` hält denselben Block als Claude-Code-Spiegelung mit Verweis auf die SSoT. opencode liest AGENTS.md mit Vorrang vor `~/.claude/CLAUDE.md`.
- **Fünf MikroTik seit 2026-08-30** — neu dazu **`ap3`** (cAP ax `cAPGi-5HaxD2HaxD`, **`10.40.99.13`**, - **Fünf MikroTik seit 2026-08-30** — neu dazu **`ap3`** (cAP ax `cAPGi-5HaxD2HaxD`, **`10.40.99.13`**,
@@ -79,11 +84,13 @@ liest, liest ihn als Erstes: er wird bei jeder Änderung mitgezogen, nicht nur d
account-weite). Abnahme über den Host-Header von einem zweiten Host: 11× 200, `nautobot` 302. account-weite). Abnahme über den Host-Header von einem zweiten Host: 11× 200, `nautobot` 302.
Offen: **Schritt 5–6** (Connector für `bf-public`, CNAME-Flip, Abbau der sechs Container) — Offen: **Schritt 5–6** (Connector für `bf-public`, CNAME-Flip, Abbau der sechs Container) —
**Runbook steht: [`tp16.3-bf-public-ap-connector-runbook.md`](tp16.3-bf-public-ap-connector-runbook.md)**, **Runbook steht: [`tp16.3-bf-public-ap-connector-runbook.md`](tp16.3-bf-public-ap-connector-runbook.md)**,
Vor-Ort-Session an ap1/ap2/ap3. 🔴 **Zwei Blocker davor, beide remote lösbar** (08-30 gemessen): Vor-Ort-Session an ap1/ap2/ap3. ✅ **Die drei §1-Blocker sind am 2026-08-31 remote erledigt** —
**(a)** der `bf-public`-Ingress zeigt auf `:80`, wo Caddy mit **308 → https** antwortet ⇒ Redirect-Schleife **(a)** Ingress auf `https://10.40.20.40:443` + `originServerName` je Regel umgestellt (die alte
über Cloudflare; Fix = `https://10.40.20.40:443` + `originServerName` je Regel (über 443 mit korrekter `:80`-Zeile hätte über CF eine Redirect-Schleife gebaut, weil Caddy dort 308 auf https antwortet);
SNI liefern alle vier Namen 200). **(b)** es gibt **keine** Freigabe `vlan99 → vlan20:443` — der **(b)** Freigabe `vlan99 → vlan20:443` **über Nautobot GC** deployt (`ip-firewall-filter` steht unter
Connector liefe in `TP5-WOULD-DROP`. Drift-Kontrolle, TP26 ⇒ kein Direkt-Write), Position vor `TP5: log-only` verifiziert, **135/135
compliant, 0 Drifts**; **(c)** `ROUTER_AP3_PASS` in `~/.secrets` nachgetragen (REST-Auth 200).
⇒ **Vor Ort startet die Arbeit bei §2/§3** (Pakete, `device-mode`, Container).
- **DNS/Resolver = AdGuard-Paar `agh-bf1`/`agh-bf2`** (LXC 110/111 auf pve, **`10.40.20.36`/`.37`**, - **DNS/Resolver = AdGuard-Paar `agh-bf1`/`agh-bf2`** (LXC 110/111 auf pve, **`10.40.20.36`/`.37`**,
TP23 Stufe 2 + TP31 Schritt 4 ✅ seit 2026-08-26): Upstream DoH zum CF-Gateway, conditional TP23 Stufe 2 + TP31 Schritt 4 ✅ seit 2026-08-26): Upstream DoH zum CF-Gateway, conditional
`[/bf.emsenhuber.net/] → 10.40.20.1`, 47 Rewrites (16 Service → Caddy `.20.40` + 10 Host-Namen `[/bf.emsenhuber.net/] → 10.40.20.1`, 47 Rewrites (16 Service → Caddy `.20.40` + 10 Host-Namen
@@ -221,7 +228,7 @@ nachmessen und dann entscheiden**. Blocker gibt es keine mehr. TP5 ist in der Na
> | TP14.4 | 🔜 | CF-Access-Token | — | > | TP14.4 | 🔜 | CF-Access-Token | — |
> | TP16 | ✅ | CF-Tunnel HA wieder voll | 2026-08-24 | > | TP16 | ✅ | CF-Tunnel HA wieder voll | 2026-08-24 |
> | TP16.2 | 🔜 | Härtung bypass-Dienste | — | > | TP16.2 | 🔜 | Härtung bypass-Dienste | — |
> | TP16.3 | 🔜 | `bf-public`-Connector auf ap1/ap2/**ap3** (Runbook steht) | — | > | TP16.3 | 🔜 | `bf-public`-Connector ap1/ap2/**ap3** (§1-Blocker ✅, Rest vor Ort) | — |
> | TP17.1 | ❌ | Shelly Gen4 → Zigbee | 2026-08-23 | > | TP17.1 | ❌ | Shelly Gen4 → Zigbee | 2026-08-23 |
> | TP17.3 | ✅ | Shelly-Regression behoben | 2026-08-23 | > | TP17.3 | ✅ | Shelly-Regression behoben | 2026-08-23 |
> | TP18 | 🔜 | Garage-Objektspeicher | — | > | TP18 | 🔜 | Garage-Objektspeicher | — |
@@ -441,12 +448,12 @@ nachmessen und dann entscheiden**. Blocker gibt es keine mehr. TP5 ist in der Na
ap1/ap2 ⇒ **zwei verschiedene `container.npk`**. Und ap3 steht auf `device-mode=basic` (nicht `home`), ap1/ap2 ⇒ **zwei verschiedene `container.npk`**. Und ap3 steht auf `device-mode=basic` (nicht `home`),
dort ist `fetch` bereits erlaubt — wir nutzen trotzdem auf allen dreien den `.tar`-Import, damit alle dort ist `fetch` bereits erlaubt — wir nutzen trotzdem auf allen dreien den `.tar`-Import, damit alle
dasselbe Image fahren (gepinnt: `cloudflared 2026.6.1`, Digest `sha256:6d91c121…`, 27,5 MB). dasselbe Image fahren (gepinnt: `cloudflared 2026.6.1`, Digest `sha256:6d91c121…`, 27,5 MB).
- 🔴 **Blocker A — Redirect-Schleife.** `bf-public` zeigt auf `http://10.40.20.40:80`; Caddy antwortet - ✅ **Blocker A — Redirect-Schleife, behoben 2026-08-31.** `bf-public` zeigt auf `http://10.40.20.40:80`; Caddy antwortet
dort mit **308 → `https://<name>/`**, also zurück durch Cloudflare in denselben Tunnel ⇒ dort mit **308 → `https://<name>/`**, also zurück durch Cloudflare in denselben Tunnel ⇒
`ERR_TOO_MANY_REDIRECTS` für alle vier Namen. **Fällt heute nicht auf**, weil die drei Alttunnel `ERR_TOO_MANY_REDIRECTS` für alle vier Namen. **Fällt heute nicht auf**, weil die drei Alttunnel
**an Caddy vorbei** direkt aufs Backend gehen — die Schleife entsteht erst mit dem Umstieg. **an Caddy vorbei** direkt aufs Backend gehen — die Schleife entsteht erst mit dem Umstieg.
Fix = `https://10.40.20.40:443` + `originServerName` je Regel (über 443 mit korrekter SNI: 4× 200). Fix = `https://10.40.20.40:443` + `originServerName` je Regel (über 443 mit korrekter SNI: 4× 200).
- 🔴 **Blocker B — die Freigabe existiert nicht.** Die `forward`-Kette des Core wurde vollständig - ✅ **Blocker B — die Freigabe fehlte, angelegt 2026-08-31 über Nautobot GC.** Die `forward`-Kette des Core wurde vollständig
gelesen (36 Regeln, Nr. 35 = `TP5: deny-by-default`): **keine** Regel `vlan99 → vlan20` für 80/443. gelesen (36 Regeln, Nr. 35 = `TP5: deny-by-default`): **keine** Regel `vlan99 → vlan20` für 80/443.
Die einzige MGMT→INFRA-Regel (Nr. 26) ist ein `src-port=8728`-**Rückweg** der API. Die einzige MGMT→INFRA-Regel (Nr. 26) ist ein `src-port=8728`-**Rückweg** der API.
⚠️ **Der Vorgänger-Runbook behauptet das Gegenteil** („Regel 27 deckt `vlan99→vlan20:80` ab ✅") — ⚠️ **Der Vorgänger-Runbook behauptet das Gegenteil** („Regel 27 deckt `vlan99→vlan20:80` ab ✅") —
@@ -454,7 +461,7 @@ nachmessen und dann entscheiden**. Blocker gibt es keine mehr. TP5 ist in der Na
in `TP5-WOULD-DROP` + Drop. Egress zu CF (Regel 13 `LAN->WAN`) und DNS (Regeln 30–33) sind dagegen ✅. in `TP5-WOULD-DROP` + Drop. Egress zu CF (Regel 13 `LAN->WAN`) und DNS (Regeln 30–33) sind dagegen ✅.
⇒ Das `masquerade` des `veth`-Netzes ist deshalb **Pflicht**, nicht Kosmetik: sonst kommen die Pakete ⇒ Das `masquerade` des `veth`-Netzes ist deshalb **Pflicht**, nicht Kosmetik: sonst kommen die Pakete
mit `172.17.0.2` am Core an, wo weder Route noch Regel sie kennt. mit `172.17.0.2` am Core an, wo weder Route noch Regel sie kennt.
- 🟡 **Blocker C — `ROUTER_AP3_PASS` fehlt in `~/.secrets/credentials.env`** (steht nur in - ✅ **Blocker C — `ROUTER_AP3_PASS` fehlte in `~/.secrets/credentials.env`, nachgetragen 2026-08-31** (steht nur in
`~/.claude.json`), gegen die Credential-Store-Konvention. Der `sshpass -e`-Reboot-Weg braucht es dort. `~/.claude.json`), gegen die Credential-Store-Konvention. Der `sshpass -e`-Reboot-Weg braucht es dort.
⚠️ Nebenbefund: **`credentials.env` lässt sich in zsh nicht sourcen** — ab ~Zeile 69 stehen rohe ⚠️ Nebenbefund: **`credentials.env` lässt sich in zsh nicht sourcen** — ab ~Zeile 69 stehen rohe
WireGuard-Zeilen, zsh bricht ab und lässt die Variablen **leer**; das sieht aus wie ein ungültiger WireGuard-Zeilen, zsh bricht ab und lässt die Variablen **leer**; das sieht aus wie ein ungültiger

View File

@@ -84,12 +84,21 @@ docs/cf-backup/cnames-tunnel-20260830-vor-tp16.3-schnitt.json # inkl. Record-I
--- ---
## §1 Drei Blocker — **remote zu erledigen, BEVOR jemand in den Keller geht** ## §1 Drei Blocker — ✅ **ALLE ERLEDIGT 2026-08-31 (remote)**
> Alle drei sind vom Schreibtisch aus lösbar. Wer sie offen lässt, steht vor Ort vor einem > ✅ **Nichts davon ist vor Ort noch zu tun.** Abschnitt bleibt als Nachweis stehen; die Fixes sind
> Connector, der `healthy` meldet, während die Seite eine Redirect-Schleife dreht. > gemessen, nicht nur abgesetzt. **Vor Ort startet die Arbeit bei §2/§3.**
>
> | Blocker | Stand | Nachweis |
> |---|---|---|
> | **A** Redirect-Schleife (`:80`) | ✅ behoben | Ingress zeigt auf `https://10.40.20.40:443`, `originServerName` je Regel gesetzt |
> | **B** Freigabe `vlan99 → vlan20:443` | ✅ behoben | Regel via Nautobot GC deployt, Plan `Completed`, am Gerät verifiziert, **135/135 compliant, 0 Drifts** |
> | **C** `ROUTER_AP3_PASS` | ✅ behoben | in `~/.secrets/credentials.env` (`chmod 600`), REST-Auth gegen ap3 = **HTTP 200** |
### 1.1 🔴 Blocker A — `bf-public` zeigt auf `:80`, und Caddy antwortet dort mit 308 ### 1.1 ✅ Blocker A — `bf-public` zeigte auf `:80`, und Caddy antwortet dort mit 308
> **Erledigt 2026-08-31.** Ingress zurückgelesen: alle vier Namen → `https://10.40.20.40:443`,
> `SNI` = der jeweilige Hostname. Der `PUT` unten ist der ausgeführte Stand, nicht mehr eine Aufgabe.
Gemessen: `curl -H "Host: vw.emsenhuber.net" http://10.40.20.40/` → **`308` → `Location: https://vw.emsenhuber.net/`**. Gemessen: `curl -H "Host: vw.emsenhuber.net" http://10.40.20.40/` → **`308` → `Location: https://vw.emsenhuber.net/`**.
@@ -117,7 +126,17 @@ curl -s -X PUT -H "Authorization: Bearer $CF_API_TOKEN" -H "Content-Type: applic
**Abnahme:** `.success == true`, danach Ingress zurücklesen und die vier `https://…:443` sehen. **Abnahme:** `.success == true`, danach Ingress zurücklesen und die vier `https://…:443` sehen.
### 1.2 🔴 Blocker B — die Firewall lässt MGMT → Caddy **nicht** durch ### 1.2 ✅ Blocker B — die Firewall liess MGMT → Caddy **nicht** durch
> **Erledigt 2026-08-31 über Nautobot Golden Config** (`gc-manual-plan.py --device crs326-core --deploy`,
> Change-ID `TP16.3-BlockerB`, Plan `23e5b667-…` **Completed**), **nicht** per Direkt-Write — `ip-firewall-filter`
> steht unter Drift-Kontrolle (TP26). Backup vorher: `core-pre-tp16.3-blockerB-20260831.backup` (TP22-Regel 3).
> Danach `ingest-core-firewall.py --apply` (Re-Baseline) + `--refresh-compliance` ⇒ **135/135, 0 Drifts.**
> **Position verifiziert:** die Regel sitzt direkt **vor** `TP5: log-only` und `TP5: deny-by-default`.
> **Funktional verifiziert:** `/tool fetch` von ap3 auf `https://10.40.20.40` erreicht Caddy und bekommt
> einen **TLS-Alert von Caddy selbst** — ein Alert kann nur vom Server kommen, das Paket ist also da.
> Der Alert ist die **SNI-Abweisung** (Gegenprobe: ohne SNI `000`, mit korrektem SNI `200`) und genau
> der Grund, warum `originServerName` in §1.1 gesetzt ist. **Kein Fehler, sondern der Beweis.**
Die `forward`-Kette des Core wurde vollständig gelesen (36 Regeln, Nr. 35 = `TP5: deny-by-default`). Die `forward`-Kette des Core wurde vollständig gelesen (36 Regeln, Nr. 35 = `TP5: deny-by-default`).
**Es gibt keine Regel `vlan99 → vlan20` für 80/443.** Die einzige Regel in dieser Richtung ist **Es gibt keine Regel `vlan99 → vlan20` für 80/443.** Die einzige Regel in dieser Richtung ist
@@ -154,7 +173,10 @@ Ohne die Regel unten läuft der Connector in Regel 34 (`TP5-WOULD-DROP`, Log) un
`veth`-Netz `172.17.0.0/24` ist dem Core unbekannt und hätte keinen Rückweg. Deshalb ist das `veth`-Netz `172.17.0.0/24` ist dem Core unbekannt und hätte keinen Rückweg. Deshalb ist das
`masquerade` in §3.4 **nicht optional**, sondern die Voraussetzung dieser Regel. `masquerade` in §3.4 **nicht optional**, sondern die Voraussetzung dieser Regel.
### 1.3 🟡 Blocker C — `ROUTER_AP3_PASS` fehlt in `~/.secrets/credentials.env` ### 1.3 ✅ Blocker C — `ROUTER_AP3_PASS` fehlte in `~/.secrets/credentials.env`
> **Erledigt 2026-08-31.** `ROUTER_AP3_USER`/`_PASS` nachgetragen, Datei `chmod 600`,
> Sicherung `~/.secrets/credentials.env.bak-20260831`. Funktionsbeweis: REST-Auth gegen ap3 = **HTTP 200**.
Vorhanden sind `ROUTER_AP1_PASS`, `ROUTER_AP2_PASS`, `CRS326_CORE_PASS`, `CRS326_BRANCH_PASS`. Vorhanden sind `ROUTER_AP1_PASS`, `ROUTER_AP2_PASS`, `CRS326_CORE_PASS`, `CRS326_BRANCH_PASS`.
Das **ap3-Passwort steht nur in `~/.claude.json`** (MCP-Env) — gegen die globale Konvention Das **ap3-Passwort steht nur in `~/.claude.json`** (MCP-Env) — gegen die globale Konvention