TP16.3 §2/§3 durch: cloudflared auf ap1/ap2/ap3, bf-public healthy mit 12 Connectoren
Merge von claude/nervous-williams-acf000 (d5b3ce3) plus Ergebnis der
Vor-Ort-Session vom 2026-08-31.
Ergebnis
- je ein cloudflared 2026.6.1 auf ap1/ap2/ap3, bf-public healthy/12
(vorher 0 Connectoren), Flash je AP 54-55 MiB frei, WLAN zurueck.
Drei Runbook-Fehler aufgedeckt und korrigiert (§2.2, §3.3, §3.5, §8)
- Flash-Budget war Faktor 2,5 zu optimistisch: cloudflared entpackt
69,5 MiB (gemessen mit docker export), nicht 28 MB -- das offizielle
Image passt auf keinen der drei APs. Ersatz: eigenes FROM-scratch-Image
mit 35,3 MiB aus dem statisch gelinkten Binary, Binary-SHA e93824f6
gepinnt, Neubau byte-identisch. tools/build-cfd-image.py.
- docker save schreibt seit Docker 25 OCI statt docker-v1 -> RouterOS
meldet "no config found in manifest". tools/oci2docker.py als Konverter.
- /container/envs nimmt list=, nicht name= (auch MikroMCPs
manage_container_env ist dadurch fuer dieses Feld unbrauchbar).
Weitere Funde
- Der device-mode-Bestaetigungs-Boot installiert keine Pakete; erst
Paket-Reboot, dann device-mode.
- "A - AVAILABLE" in /system package print ist ein Download-Katalog,
kein Paket auf dem Geraet (Gegenprobe am Core: 2,9 MB frei, listet
10,1 MB wifi-qcom als verfuegbar).
- MikroMCPs export_config exportiert unvollstaendig (301 B statt 6,2 KiB).
Offen und entscheidungsbeduerftig
- Compliance 128/135: §3.4 verlangt Direkt-Writes fuer ip address/nat,
die unter GC-Kontrolle stehen -> sechs neue extra-Drifts, zwei je AP.
Inhaltlich richtig, gehoeren ins Soll -- kein Re-Baseline, kein Rueckbau.
- Zwei Nautobot-Tokens sind in dieser Session ins Transkript geraten
(unzureichende sed-Maskierung) -> Rotation noetig, TP30. Merkregel in
CLAUDE.md ergaenzt.
Merge-Konflikte aufgeloest
- Nummernkonflikt: beide Branches hatten ein TP37. main behaelt TP37
(Nautobot als Netzwerkdoku, User-Okay 08-30), OOB-Mgmt wird TP38.
Braucht noch Christophs Okay.
- Drei CF-Rollback-Snapshots hatten gleichen Namen bei verschiedenem
Inhalt -> aelterer Stand als *-vor-hostname-schnitt.json erhalten,
keiner verworfen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -27,12 +27,21 @@ Mgmt (VLAN99, Ist 2026-08-16): core `.99.1` · branch `.99.2` · ap1 `.99.11` ·
|
||||
|
||||
## Ist-Stand (Kurz)
|
||||
|
||||
**Stand 2026-08-28 — live gegen die Geräte geprüft (MikroMCP/REST), nicht aus der Doku abgeschrieben.**
|
||||
**Die Compliance-Zeile unten ist am 2026-08-23 gegen die Nautobot-API nachgezogen.**
|
||||
**Stand 2026-08-31 — live gegen die Geräte geprüft (MikroMCP/REST/SSH), nicht aus der Doku abgeschrieben.**
|
||||
**Compliance am 2026-08-31 nach vollem Refresh (Backup→Intended→Compliance) gelesen: 128/135.**
|
||||
*(Die Fassung vom 2026-08-16 stand bis zum 18. hier und war an vier Stellen überholt. Wer diesen Block
|
||||
liest, liest ihn als Erstes: er wird bei jeder Änderung mitgezogen, nicht nur die TP-Abschnitte.)*
|
||||
- 🆕 **MCP-Bestand 2026-08-28 korrigiert:** **9 MCPs identisch** in `~/.claude.json` und `~/.config/opencode/opencode.jsonc` (vorher Asymmetrie 7 vs. 11 — `portainer-mcp`/`shelly-mcp` raus, `gitea-mcp`/`n8n-mcp` rein). **Mac mini = n8n-Host, NICHT zentraler MCP-Host** (Performance-Restriktion); zentraler MCP-Host = **DS923** (`nas-bf`, Portainer Endpoint 14). Inventur-Stand `mcp/INVENTORY.md` + Policy `docs/mcp-host-policy.md`.
|
||||
- 🆕 **MCP-Migration FQDN→IP (2026-08-29, TP35 Schicht 1):** `gitea-mcp` und `n8n-mcp` in `~/.config/opencode/opencode.jsonc` von CF-FQDN auf LAN-IP umgestellt — `gitea-mcp` → `http://10.40.20.167:3000/mcp` (macmini, **ohne** Bearer, intern), `n8n-mcp` → `http://10.40.20.167:3001/mcp` (mit `Bearer {env:N8N_MCP_BEARER}`). **Befund dabei:** der Handoff 1.2 nannte für gitea-mcp die falsche IP `.157:3000` (das ist die Gitea-Web-UI, `400 Invalid CSRF`); der echte gitea-mcp läuft auf `.167:3000` (`serverInfo: gitea-mcp 0.0.10`). `cloudflare`-MCP bleibt auf dem offiziellen FQDN (User-Direktive).
|
||||
- 🆕 **TP16.3 §2/§3 durchgezogen (2026-08-31, Vor-Ort):** je ein `cloudflared`-Container auf
|
||||
**ap1/ap2/ap3**, `bf-public` **healthy mit 12 Connectoren** — der Tunnel hat damit zum ersten Mal
|
||||
überhaupt Connectoren. 🔴 **Zwei Runbook-Fehler dabei aufgedeckt:** die Flash-Rechnung war um Faktor
|
||||
2,5 zu optimistisch (entpacktes cloudflared = **69,5 MiB**, nicht 28 MB ⇒ das offizielle Image passt
|
||||
auf keinen der drei APs; Ersatz ist ein selbst gebautes `FROM scratch`-Image mit 35,3 MiB, Binary-SHA
|
||||
`e93824f6…` gepinnt), und `/container/envs` heisst `list=`, nicht `name=`.
|
||||
🔴 **Offen: Compliance 128/135.** §3.4 verlangt Direkt-Writes für `ip address`/`nat`, die unter
|
||||
GC-Kontrolle stehen ⇒ **sechs neue `extra`-Drifts** (zwei je AP), inhaltlich richtig, aber nicht im
|
||||
Soll. Braucht eine Entscheidung (ins Template heben), **kein Re-Baseline**. Details im TP16.3-Abschnitt.
|
||||
- 🆕 **TP37 angelegt (2026-08-30, User-Okay):** Nautobot soll durchgehende Netzwerkdoku werden, erster
|
||||
Anwendungsfall die CF-Connectoren. 🔴 **Befund dabei, live gegen die API:** Nautobot kennt heute
|
||||
**nur die 8 Netzgeräte** (4 aktiv, 4 decommissioned) — `ipam.Service`, `CustomField`, `Relationship`,
|
||||
@@ -51,10 +60,38 @@ liest, liest ihn als Erstes: er wird bei jeder Änderung mitgezogen, nicht nur d
|
||||
laut Entscheid auf **ap1/ap2**, dort fehlen aber `container`-Paket **und** `device-mode container=yes`
|
||||
(physische Bestätigung nötig). Bis dahin stehen `bf-mm`/`bf-nas`/`bf-pve` mit **6 Containern**.
|
||||
Backups: `docs/cf-backup/*-20260830-vor-tp16.3-schnitt.json`.
|
||||
- 🆕 **TP16.3/TP31 Schritt 5 vorbereitet (2026-08-30):** Runbook für die Vor-Ort-Session geschrieben
|
||||
([`tp16.3-bf-public-ap-connector-runbook.md`](tp16.3-bf-public-ap-connector-runbook.md)), CF-Rollback-Snapshots
|
||||
unter `docs/cf-backup/*-20260830-vor-tp16.3-schnitt.json` **neu angelegt** (es gab sie vorher nicht).
|
||||
`docs/tp31-schritt5-runbook.md` ist damit **abgelöst** (read-only-Header): es kannte ap3 nicht,
|
||||
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
|
||||
öffentlichen Namen laufen noch über bf-mm/bf-nas/bf-pve **an Caddy vorbei** direkt aufs Backend.
|
||||
- 🆕 **TP38 angelegt (2026-08-31):** OOB-Management — je ein untagged-VLAN99-Port auf beiden Switches.
|
||||
Auslöser: beim Core-Neustart war `crs326-branch` nicht erreichbar, weil sein Mgmt **ausschließlich**
|
||||
über den Core läuft (`vlan99` dort `tagged=ether1,ether24,bridge`, kein untagged-Member).
|
||||
🔴 **Nebenbefund mit eigenem Gewicht: `crs326-branch` hat gar keine `input`-Firewallregeln** — sein
|
||||
Schutz ist heute allein die Erreichbarkeit über den gehärteten Core. Das ist die Hauptauflage des TPs.
|
||||
- 🆕 **ROS-Upgrade 7.24.1 auf allen fünf (2026-08-31, User).** In Nautobot nachgezogen: **ap1** stand
|
||||
als einziges Gerät noch auf `7.24` (die anderen vier waren schon richtig) ⇒ auf `7.24.1` gesetzt.
|
||||
🔴 **Eine Drift kam mit dem Upgrade: `crs326-core` / `wifi-security`** — `sec-main` hat am Gerät
|
||||
`ft-over-ds=no`, Soll ist `yes` (`ft=yes` steht korrekt). **134/135 compliant.** Bewusst **nicht**
|
||||
angefasst: das ist eine Roaming-Einstellung und braucht eine Entscheidung (Gerät korrigieren **oder**
|
||||
den neuen Wert zum Soll erklären) plus Re-Provisionierung der CAPs — **nicht nebenbei**.
|
||||
⚠️ Ein Re-Baseline ohne Entscheid würde die Drift-Erkennung wertlos machen.
|
||||
- 🆕 **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`.
|
||||
|
||||
- **Alle vier MikroTik laufen auf RouterOS 7.24** (stable, 2026-08-17 von Christoph selbst gehoben, TP27),
|
||||
**RouterBOOT ebenfalls 7.24** auf allen vieren (`current-firmware` = `upgrade-firmware`, geprüft 08-18).
|
||||
- **Fünf MikroTik, alle auf RouterOS `7.24.1`** (User-Upgrade 2026-08-31, live gegengelesen;
|
||||
RouterBOOT jeweils = `upgrade-firmware` = `7.24.1`). Neu dabei **`ap3`** (cAP ax `cAPGi-5HaxD2HaxD`,
|
||||
**`10.40.99.13`**, Uplink core `ether21`).
|
||||
✅ **Folge für TP16.3:** die versionsgebundene `container.npk` ist damit **eine** Datei für alle drei
|
||||
APs (alle `arm64`) — die Zwei-Dateien-Regel vom 08-30 war rein versionsbedingt und ist erledigt.
|
||||
*(Hier stand bis 08-29 „Alle vier MikroTik … 7.24" — durch ap3 und das Upgrade zweifach überholt.)*
|
||||
- **L3 + WAN-Edge = `crs326-core`** (CRS326-24G-2S+, Technik UG): Routing, DHCP (7 Server,
|
||||
54 Reservierungen), DNS, NAT, **NTP-Server für den Standort** und der WG-Hub (TP10, Peers `fm`/`dw`)
|
||||
laufen hier. Mgmt-SVI **`10.40.99.1`** (vlan99), Gateway `.1` je VLAN. **WAN = `vlan100`** — DHCP-Client
|
||||
@@ -94,7 +131,15 @@ liest, liest ihn als Erstes: er wird bei jeder Änderung mitgezogen, nicht nur d
|
||||
**16 Sites** (4 public, 12 intern mit `.bf.`-Suffix), Zertifikate ausschliesslich
|
||||
per **DNS-01** mit einem eng geschnittenen `Zone:DNS:Edit`-Token (`CF_DNS_API_TOKEN`, **nicht** das
|
||||
account-weite). Abnahme über den Host-Header von einem zweiten Host: 11× 200, `nautobot` 302.
|
||||
Offen: Schritt 5–6 (Tunnel `bf-public`, Abbau).
|
||||
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)**,
|
||||
Vor-Ort-Session an ap1/ap2/ap3. ✅ **Die drei §1-Blocker sind am 2026-08-31 remote erledigt** —
|
||||
**(a)** Ingress auf `https://10.40.20.40:443` + `originServerName` je Regel umgestellt (die alte
|
||||
`:80`-Zeile hätte über CF eine Redirect-Schleife gebaut, weil Caddy dort 308 auf https antwortet);
|
||||
**(b)** Freigabe `vlan99 → vlan20:443` **über Nautobot GC** deployt (`ip-firewall-filter` steht unter
|
||||
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`**,
|
||||
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
|
||||
@@ -150,6 +195,11 @@ Public-Strang auf `bf-public` zu heben und die drei Alt-Tunnel samt sechs Contai
|
||||
> - 🔜 **Session B** — shelly-mcp-Container auf DS923 (`10.40.20.10`) deployen (live, ssh). Repo-Materialien committed (`mcp/shelly/{Dockerfile,BUILD.md,requirements.txt,.gitignore}`); `.env` und Tunnel-Secret lokal — kein MCP-Client (User-Direktive).
|
||||
> - 🔜 **Session C** — `caddy/cf-sync.py`-Drift-Check + Backup-Guard (`~/dev/.bin/backup-guard.sh`, TP35 Schicht 5).
|
||||
>
|
||||
> 🆕 **Dazu seit 2026-08-30: TP16.3/TP31 Schritt 5 = Vor-Ort-Session** an ap1/ap2/**ap3**
|
||||
> ([Runbook](tp16.3-bf-public-ap-connector-runbook.md)). **Reihenfolge beachten:** die drei Blocker in
|
||||
> §1 sind **remote** zu erledigen, *bevor* jemand in den Keller geht — sonst steht man vor einem
|
||||
> Connector, der `healthy` meldet, während die Seite eine Redirect-Schleife dreht.
|
||||
>
|
||||
> 🆕 **Strukturelle Folge = TP36:** SSoT für `teilprojekte.md`-Live-Daten via Nautobot (eigene Session, nicht heute). Befund 2026-08-28: manuell gepflegte Datei verliert gegen die Realität, sobald sie lang wird. Quickref-Index, TP-Abschnitt und Ist-Stand-Block müssen bei jedem Edit mitgezogen werden — keine schlägt die andere. **Merkregel im Lessons-Block.**
|
||||
|
||||
> 🆕 **Neu am 2026-08-24/25:** [TP31](#tp31) löst **TP16** und **TP16.2** ab und dampft cloudflared
|
||||
@@ -253,7 +303,7 @@ nachmessen und dann entscheiden**. Blocker gibt es keine mehr. TP5 ist in der Na
|
||||
> | TP14.4 | 🔜 | CF-Access-Token | — |
|
||||
> | TP16 | ✅ | CF-Tunnel HA wieder voll | 2026-08-24 |
|
||||
> | TP16.2 | 🔜 | Härtung bypass-Dienste | — |
|
||||
> | TP16.3 | ⏳ | cloudflared-Reduktion (14 Hostnames geschnitten 08-30, F1 durch; Abbau braucht Vor-Ort-Session) | — |
|
||||
> | TP16.3 | ⏳ | cloudflared-Reduktion (14 Hostnames geschnitten 08-30, F1 durch; §1–§3 durch 08-31, `bf-public` healthy/12 — offen: Flip §5, Abbau §6) | — |
|
||||
> | TP17.1 | ❌ | Shelly Gen4 → Zigbee | 2026-08-23 |
|
||||
> | TP17.3 | ✅ | Shelly-Regression behoben | 2026-08-23 |
|
||||
> | TP18 | 🔜 | Garage-Objektspeicher | — |
|
||||
@@ -264,13 +314,14 @@ nachmessen und dann entscheiden**. Blocker gibt es keine mehr. TP5 ist in der Na
|
||||
> | TP28 | ✅ | WLAN/CAPsMAN — dritter AP `ap3` (cAP ax) **live** | 2026-08-30 |
|
||||
> | TP29 | 💡 | Anwesenheitserkennung (Idee) | — |
|
||||
> | TP30 | 🔜 | Credentials (Etappe 1 ✅, Rest gebündelt) | — |
|
||||
> | TP31 | ✅ | Reverse Proxy Caddy (Schritt 1–4 ✅) | — |
|
||||
> | TP31 | ⏳ | Reverse Proxy Caddy (1–4 ✅, 5–6 = Vor-Ort-Session) | — |
|
||||
> | TP32 | 🟢 | Nummern-Systematik (Konzept) | — |
|
||||
> | TP33 | 💡 | Sonos fern (Idee, zurückgestellt) | — |
|
||||
> | TP34 | 🔜 | Caddyfile via Nautobot/GC (Stub, Inventur-Handoff 2026-08-27) | — |
|
||||
> | TP35 | ⏳ | MCP-Schichtung + 3-Schicht-CLAUDE.md + Backup-Guard (Schicht 1+3+6 ✅) | — |
|
||||
> | TP36 | 🔜 | SSoT via Nautobot für teilprojekte.md-Live-Daten + Datei-Aufteilung | — |
|
||||
> | TP37 | 🔜 | Nautobot als Netzwerkdoku: Hosts/Container/Dienste + CF-Connectoren (Okay 08-30) | — |
|
||||
> | TP38 | 🔜 | OOB-Mgmt: untagged-VLAN99-Port je Switch | — |
|
||||
> | — | — | **34 archivierte TPs** in `teilprojekte-archiv.md` | — |
|
||||
|
||||
### TP5.1 — DNS-Visibility & Flow-Observability für Low-Trust-Geräte (Airfryer-Fall) ▸ vorgemerkt
|
||||
@@ -463,7 +514,49 @@ nachmessen und dann entscheiden**. Blocker gibt es keine mehr. TP5 ist in der Na
|
||||
- **Reihenfolge:** (1) Client-IP-Weitergabe fixen (inkl. HA `trusted_proxies`) → (2) `/admin`-Pfade path-scoped schützen → (3) App-Limits + 2FA → (4) Edge-Rules → (5) CrowdSec/fail2ban mit CF-Bouncer.
|
||||
- **Abh.:** TP16 ✅ (Tunnel-HA). Verwandt: **TP20.1** (vw wandert auf Platform-Postgres — DB-HA ≠ Access, getrennt halten), **TP23** (dessen WARP-Managed-Network-Idee ist durch diesen Constraint **entlastet**: `vw` verlangt keine Device-Identity, also kollidiert „WARP zuhause aus" nicht mehr mit dem Access-Strang).
|
||||
|
||||
### TP16.3 — cloudflared-Reduktion: was der Mesh als Datenpfad kann (und was nicht) ▸ ⏳ **GROSSER SCHNITT DURCH (2026-08-30), Public-Strang blockiert** — `docs/tp16.3-nautobot-mesh-only-runbook.md §8`
|
||||
### TP16.3 — cloudflared-Reduktion: was der Mesh als Datenpfad kann (und was nicht) ▸ ⏳ **§1–§3 DURCH (2026-08-31): drei Connectoren live, `bf-public` healthy/12** — offen: Flip §5, Abbau §6 — `docs/tp16.3-nautobot-mesh-only-runbook.md §8`
|
||||
|
||||
#### 🔜 Follow-up F2 — `bf-public`-Connector auf die APs (Runbook 2026-08-30)
|
||||
**Runbook: [`tp16.3-bf-public-ap-connector-runbook.md`](tp16.3-bf-public-ap-connector-runbook.md)**
|
||||
— Vor-Ort-Session an **ap1 + ap3 (Technik UG)** und **ap2 (Technik OG)**, weil der
|
||||
`device-mode`-Wechsel eine **physische** Bestätigung am Gerät verlangt.
|
||||
|
||||
- 🆕 **ap3 ist neu und nicht baugleich** (live 08-30): **cAP ax** (`cAPGi-5HaxD2HaxD`, `10.40.99.13`),
|
||||
arm64, 96 MB Flash frei, 689 MB RAM frei. ~~Zwei verschiedene `container.npk`~~ — **überholt seit dem
|
||||
ROS-Upgrade 08-31: alle drei auf `7.24.1`/arm64 ⇒ EINE Datei** (`container-7.24.1-arm64.npk`,
|
||||
1.187.985 B — exakt die Grösse, die die APs selbst für das Paket melden, damit belegt statt
|
||||
angenommen). ap3 steht auf `device-mode=basic` (nicht `home`), dort ist `fetch` bereits erlaubt —
|
||||
wir nutzen trotzdem auf allen dreien den `.tar`-Import, damit alle dasselbe Image fahren.
|
||||
- ✅ **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 ⇒
|
||||
`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.
|
||||
Fix = `https://10.40.20.40:443` + `originServerName` je Regel (über 443 mit korrekter SNI: 4× 200).
|
||||
- ✅ **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.
|
||||
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 ✅") —
|
||||
Regel 27 ist `IOT->INFRA http-rueckweg`, `in-interface=vlan30`. Ohne neue Regel läuft der Connector
|
||||
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
|
||||
mit `172.17.0.2` am Core an, wo weder Route noch Regel sie kennt.
|
||||
- ✅ **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.
|
||||
⚠️ 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
|
||||
Token. Alle Snippets im Runbook laufen deshalb in `bash -c`.
|
||||
- 🔩 **Kein PoE als Abkürzung:** beide CRS326 haben nachweislich gar kein PoE-Menü
|
||||
(`bad command name poe`) ⇒ es gibt **keinen** Remote-Power-Cycle, die Bestätigung ist wirklich physisch.
|
||||
- 🧹 **Abbau (frühestens 7 Tage nach dem Flip):** Tunnel `bf-mm`/`bf-nas`/`bf-pve` löschen und die
|
||||
**sechs** Container entfernen — LXC 400 (`cloudflared-bfmm`, `-bfnas`), macmini (`-bfpve`, `-bfnas`),
|
||||
NAS ep14 (`-bfpve`, `-bfmm`); alle drei Hosts 08-30 verifiziert. **Erst Container, dann Tunnel** —
|
||||
umgekehrt erzeugt man 530er. 🚫 **`terpene-cloudflared` (LXC 400) und `fm.emsenhuber.net` bleiben.**
|
||||
- 🔴 **Ehrliche Einschränkung (aus TP31, gilt unverändert):** solange Caddy nur auf `caddy-bf1` liegt,
|
||||
ist das Origin aller drei Connectoren **derselbe Rechner** — drei Connectoren kaufen
|
||||
**Hardware-Diversität am Eingang, aber keine Verfügbarkeit**. Der echte Gewinn kommt mit `caddy-bf2`
|
||||
(LXC 131); für `vw` gilt dabei die TP20.1-Bremse (Schreibzugriffe auf einen read-only Standby).
|
||||
- **Rollback:** `docs/cf-backup/*-20260830-vor-tp16.3-schnitt.json` (vier Ingress-Configs + CNAMEs
|
||||
inkl. Record-IDs), am 2026-08-30 **neu angelegt**.
|
||||
- ▶️ **Schnitt ausgeführt 2026-08-24:** `nautobot.emsenhuber.net` ist aus dem Ingress von `bf-nas`
|
||||
entfernt (**8 → 7 Regeln**, Catch-all weiterhin letzte). Rollback-Sicherung liegt unter
|
||||
`docs/cf-backup/bf-nas-ingress-20260824-vor-tp16.3.json` — Rückweg ist ein `PUT`, eine Minute.
|
||||
@@ -488,6 +581,58 @@ nachmessen und dann entscheiden**. Blocker gibt es keine mehr. TP5 ist in der Na
|
||||
(mit den CF-Leichen am 08-29 gelöscht, Commit `39ba5da`) — der Dienst ist vollständig aus dem
|
||||
öffentlichen Strang heraus und ausschließlich über `10.40.20.30:8080` bzw. intern über Caddy erreichbar.
|
||||
|
||||
##### ✅ §2/§3 durchgezogen — Vor-Ort-Session 2026-08-31
|
||||
|
||||
**Ergebnis:** je ein `cloudflared` auf ap1/ap2/ap3, `bf-public` **healthy mit 12 Connectoren**
|
||||
(3 APs × 4 CF-Edges, alle `2026.6.1`, Egress über `90.146.33.177`). WLAN auf allen drei zurück
|
||||
(CAPs `state=Ok`), Flash je AP **54–55 MiB frei**.
|
||||
|
||||
- 🔴 **Die Platzrechnung des Runbooks war falsch — Faktor 2,5.** §3.5 rechnete mit „28 MB `.tar`
|
||||
+ ~28 MB entpackte Layer ≈ 39 MB Rest". Die 28 MB sind die **komprimierte** Archivgrösse; das
|
||||
entpackte Image misst **72.926.208 B = 69,5 MiB** (`docker export | wc -c`). Spitzenbedarf beim
|
||||
Import ist **Tar + entpacktes Abbild gleichzeitig** ⇒ 26,2 + 69,5 = **95,7 MiB gegen 90,5 MiB frei**.
|
||||
Das offizielle `cloudflare/cloudflared`-Image passt auf diese Geräte **nicht** — auf keinem der drei.
|
||||
⚠️ **`docker image inspect .Size` führt hier in die Irre** (meldete 27.463.950); `docker history`
|
||||
summiert auf ~70 MB. **Für Flash-Budgets `docker export` messen, nicht `inspect`.**
|
||||
- ✅ **Lösung: eigenes Minimal-Image, `FROM scratch`.** Das Binary im offiziellen Image ist
|
||||
**statisch gelinkt** (verifiziert) und 36,8 MB gross; die restlichen ~33 MB sind distroless-Debian,
|
||||
das cloudflared nicht braucht. Das selbst gebaute Archiv (Binary + CA-Bundle) misst **35,3 MiB**,
|
||||
Spitzenbedarf 70,6 MiB. Gepinnt über die **Binary-SHA256 `e93824f6…`** — cloudflared bestätigt sie
|
||||
beim Start selbst (`Version 2026.6.1 (Checksum e93824f6…)`), das ist der Reproduzierbarkeits-Nachweis.
|
||||
Build-Skript erzeugt das docker-v1-Archiv direkt, ohne Docker-Umweg.
|
||||
- 🔴 **`docker save` taugt seit Docker 25 nicht mehr als Quelle.** Mit containerd-Image-Store schreibt
|
||||
es ein **OCI-Layout** (`blobs/sha256/`, gzip-Layer); RouterOS will docker-v1 und quittiert das mit
|
||||
**`no config found in manifest`** — eine Meldung, die nach kaputtem Image klingt, nicht nach Format.
|
||||
- 🔴 **`/container/envs` heisst auf ROS 7.24.1 `list=`, nicht `name=`.** Das Runbook (§3.5) und das
|
||||
MikroMCP-Tool `manage_container_env` benutzen beide `name=` und scheitern mit
|
||||
`unknown parameter name`. Token-Übergabe deshalb per `.rsc` + `/import` (Datei danach löschen) —
|
||||
so steht der Wert weder in `ps` noch im Transkript.
|
||||
- 🔴 **Der `device-mode`-Bestätigungs-Boot installiert KEINE Pakete.** Auf ap1 wurde die `.npk` vor
|
||||
dem Setzen von `device-mode` hochgeladen, um Christoph einen Reboot zu sparen: der Stromzyklus hat
|
||||
`container: yes` bestätigt und die Datei **verbraucht, ohne sie zu installieren** — `/system package
|
||||
print` zeigte danach nur zwei Pakete. **Reihenfolge ist also Pflicht: erst Paket-Reboot, dann
|
||||
`device-mode`.** Nachholen ging ohne physischen Gang (SSH-Reboot).
|
||||
- 🟡 **`A - AVAILABLE` bei `/system package print` ist ein Download-Katalog, kein Paket auf dem Gerät.**
|
||||
Gegenprobe: `crs326-core` hat 2,9 MB frei von 16 MB, listet aber `wifi-qcom` (10,1 MB) und
|
||||
`rose-storage` (4,3 MB) als „available"; die Grössen sind zudem arch-spezifisch. Ein
|
||||
`/system package enable` löst also einen Internet-Fetch aus — auf ap1/ap2 per `device-mode=home`
|
||||
gesperrt. **Der `.npk`-Weg bleibt richtig.**
|
||||
- 🟡 **MikroMCPs `export_config` liefert einen unvollständigen Export** (301 B gegen 6,2 KiB bei
|
||||
direktem `/export`) — ein Rollback-Artefakt, das existiert und trotzdem leer ist. Für die Backups
|
||||
dieser Session wurden die MCP-Exporte verworfen und `/export file=…` verwendet.
|
||||
- 🔴 **Offen und entscheidungsbedürftig: Compliance steht auf 128/135, nicht 134/135.** §3.4 schreibt
|
||||
Direkt-Writes für `veth` / `ip address` / `nat` vor, während `/ip address` und `/ip firewall` unter
|
||||
GC-Drift-Kontrolle stehen — **Runbook und Governance widersprechen sich.** Folge: je AP zwei neue
|
||||
`extra`-Drifts (`ip-address` mit `172.17.0.1/24 interface=veth-cf`, `ip-firewall-nat` mit dem
|
||||
`TP31: cloudflared egress`-Masquerade), also **sechs**. Sie sind **inhaltlich richtig** — die Config
|
||||
soll dort stehen. Der saubere Weg ist, sie ins Soll zu heben (AP-Template/ConfigContext), **nicht**
|
||||
ein Re-Baseline und **nicht** ein Rückbau. ⚠️ Bis dahin ist die Zahl erklärt, aber nicht in Ordnung.
|
||||
(Die siebte Drift, `crs326-core` / `wifi-security` / `ft-over-ds`, ist die bekannte und unverändert.)
|
||||
- ⚠️ **WLAN-Abnahme mit Einschränkung:** `main` (3) und `iot` (27) haben Clients und frische Leases;
|
||||
`media`/`untrusted`/`guest` hatten **schon vor** dem Umbau null Clients — für die drei gibt es
|
||||
keinen Lease-Nachweis, weil niemand eine zieht. Kein Rückschritt, aber auch kein Beleg.
|
||||
|
||||
|
||||
#### ▶️ Der grosse Schnitt — ausgefuehrt 2026-08-30 (User-Entscheid „kompletter Schnitt in einem Zug")
|
||||
- **12 Hostnames entfernt**, aus Ingress **und** DNS, in einem Durchgang:
|
||||
`pve` · `portainer` · `pve-mcp` · `ha-mcp` · `ds-bf` · `ccu` · `iobroker` · `z2m25` · `pbs`
|
||||
@@ -2347,6 +2492,78 @@ kann in Ruhe nachziehen.
|
||||
- **Verzahnt mit:** TP22 (Nautobot-GC als Vorbild), TP35 (geht Hand in Hand — beide sind „SSoT + Konsolidierung"), `bf-infra/CLAUDE.md`-Lessons.
|
||||
- **Referenzen:** Lessons-Block-Eintrag „Manuell gepflegte Doku mit Live-Daten verliert gegen die Realität" · Handoff Session A 2026-08-28.
|
||||
|
||||
<a id="tp37"></a>
|
||||
### TP38 — Out-of-Band-Management: ein untagged-VLAN99-Port je Switch ▸ 🔜 **ANGELEGT 2026-08-31**
|
||||
|
||||
> **Auslöser (User 2026-08-31, aus eigener Anschauung):** *„ich durfte beim Neustart vom core gerade
|
||||
> erleben, dass ich nicht mehr auf den branch zum verbinden kam. weil das eben alles über den core
|
||||
> läuft."*
|
||||
|
||||
#### Der Befund, live nachgemessen (2026-08-31)
|
||||
Das Management des Standorts hat **einen einzigen Weg**, und der führt über den Core:
|
||||
|
||||
| | `crs326-core` | `crs326-branch` |
|
||||
|---|---|---|
|
||||
| Mgmt-IP | `10.40.99.1` (VLAN99-SVI) | `10.40.99.2` (`vlan99` auf der Bridge) |
|
||||
| VLAN99 auf der Bridge | SVI + Trunks | **`tagged=ether1,ether24,bridge`**, `untagged=""` |
|
||||
| Weg dorthin | direkt | **nur über `ether1` → core `ether8`** |
|
||||
|
||||
⇒ **`crs326-branch` ist ausschließlich durch den Core hindurch erreichbar.** Rebootet der Core, ist der
|
||||
Branch für die Dauer weg — nicht weil er ausfällt, sondern weil der einzige Pfad zu ihm fehlt. Dasselbe
|
||||
gilt für jeden Fehler, der den Core-Trunk oder dessen VLAN99-Konfiguration betrifft. **Wer den Core
|
||||
zerkonfiguriert, verliert den Branch gleich mit** — und damit die Möglichkeit, es vom Branch aus zu
|
||||
reparieren.
|
||||
|
||||
🔴 **Der zweite Befund ist der unangenehmere: `crs326-branch` hat KEINE `input`-Firewallregeln** (Kette
|
||||
leer, live gelesen). Sein gesamter Schutz besteht heute darin, dass VLAN99 nur über den Core erreichbar
|
||||
ist und **dessen** `forward`-Kette seit TP5 deny-by-default fährt. **Die Erreichbarkeitsbeschränkung
|
||||
IST die Zugangskontrolle.** Das ist für die Lösung unten kein Detail, sondern die Hauptauflage.
|
||||
|
||||
#### Zielbild
|
||||
**Je Switch ein Port, untagged in VLAN99** — ein Laptop direkt daran erreicht das Gerät ohne jeden
|
||||
Umweg über den Core, ohne Trunk, ohne funktionierendes Routing.
|
||||
|
||||
```
|
||||
Laptop ──(statische IP 10.40.99.x/24)── ether23 (untagged VLAN99) ── crs326-branch 10.40.99.2
|
||||
^ funktioniert auch, wenn der Core steht
|
||||
```
|
||||
|
||||
**Kandidat `ether23` auf beiden Geräten** — auf core **und** branch heute `running=false`, also frei,
|
||||
und symmetrisch (ein Port, eine Nummer, an beiden Standorten dieselbe). *(Core hat reichlich freie
|
||||
Ports; auf dem Branch sind `ether23` und die Reihe darunter frei, `ether24` ist ap2.)*
|
||||
|
||||
#### Umsetzung (Skizze — eigene Session)
|
||||
1. **Port aus dem Trunk-Verhalten nehmen und in VLAN99 untaggen:**
|
||||
`bridge port` mit `pvid=99` + `frame-types=admit-only-untagged-and-priority-tagged`,
|
||||
dazu `ether23` als `untagged`-Member in die `bridge vlan`-Zeile für VLAN99.
|
||||
2. **Kein DHCP dort** — der VLAN99-DHCP läuft auf dem Core, und genau der soll ja nicht Voraussetzung
|
||||
sein. ⇒ **statische IP am Laptop** (`10.40.99.50/24`), im Runbook mitschreiben.
|
||||
3. **Beschriftung.** Ein OOB-Port, den niemand erkennt, wird im Zweifel für „ist ja frei" gehalten.
|
||||
4. **Abnahme, die den Zweck wirklich prüft:** Laptop an `ether23` des Branch, **Core-Trunk `ether1`
|
||||
gezogen** (oder Core im Reboot) → `10.40.99.2` muss antworten. Alles andere misst nur den Normalfall.
|
||||
|
||||
#### 🔴 Auflage, ohne die das TP nicht gebaut werden darf
|
||||
Ein untagged-MGMT-Port ist **ein physischer Generalschlüssel**: wer ihn steckt, steht ungefiltert im
|
||||
Management-VLAN eines Geräts, das (Branch) keinerlei `input`-Filter hat. Deshalb gehört **eine** der
|
||||
folgenden Absicherungen dazu, und die Entscheidung darüber in die Umsetzungs-Session:
|
||||
- **Port `disabled=yes` im Normalbetrieb**, im Bedarfsfall am *anderen* Gerät freigeschaltet — schützt,
|
||||
kostet aber genau dann Zugriff, wenn das andere Gerät weg ist (halbierter Nutzen, ehrlich benennen);
|
||||
- **oder** `input`-Regeln auf beiden Switches nachziehen (der Branch braucht sie ohnehin — das ist die
|
||||
eigentliche Lücke, die dieses TP nebenbei aufgedeckt hat);
|
||||
- **oder** rein physisch: abschließbarer Schrank, Port beschriftet und dokumentiert.
|
||||
|
||||
⚖️ **Meine Einschätzung:** die `input`-Regeln sind der richtige Weg, weil sie eine Lücke schließen, die
|
||||
**unabhängig von diesem TP** besteht. Der OOB-Port macht sie nur sichtbar.
|
||||
|
||||
#### Abgrenzung
|
||||
- **Nicht** Teil von TP16.3 — dort wird nur festgestellt, dass dreimal Geräte rebooten, deren Mgmt am
|
||||
Core hängt. TP37 entschärft das für künftige Sessions, ist aber **keine Voraussetzung** dafür.
|
||||
- **Nicht** VLAN-Schema-Änderung (TP32 bleibt unberührt), **nicht** Konsolen-/Serial-Zugang (das wäre
|
||||
echtes OOB im engeren Sinn; hier geht es um ein zweites *Netz*-Bein).
|
||||
- **Abh.:** TP5 (Firewall-Systematik, liefert das Muster für die `input`-Regeln), TP15 (Portbelegung
|
||||
beider CRS326), TP22 (die Umsetzung läuft über Golden Config, `ip-firewall-filter` und
|
||||
`bridge`-Sektionen stehen unter Drift-Kontrolle).
|
||||
|
||||
---
|
||||
|
||||
### TP37 — Nautobot als durchgehende Netzwerkdoku: Hosts/Container/Dienste, erster Anwendungsfall CF-Connectoren ▸ 🔜 ANGELEGT 2026-08-30 (User-Okay erteilt)
|
||||
|
||||
Reference in New Issue
Block a user