Der Branch enthaelt jetzt origin/main (0680cca, TP25 Fund E / TP27) und die Korrekturc8b2a15aus claude/nervous-williams-acf000. Inhaltliche Korrektur an meinen eigenen Dateien - Ich hatte die wifi-security-Drift (ft-over-ds=no auf sec-main) dem 7.24.1-Upgrade zugeschrieben. Falsch: gc-transaktion.py status fuehrt TP25-fund-e-ft-over-ds als OFFEN -- der Hebel wurde 08:09 bewusst gesetzt, der Core erst 14:36 gehoben. Sie gehoert einer laufenden TP25-Session, braucht keine Entscheidung und darf nicht gruen gemacht werden. Korrigiert im §5-Prompt und im §2/§3-Handoff. - Merkregel in CLAUDE.md: vor jeder Drift-Bewertung gc-transaktion.py status lesen. Dazu die Abgrenzung Transaktion (befristeter Versuch) vs. Soll/Template (Dauerzustand) -- die sechs AP-Drifts aus TP16.3 §3.4 sind Dauerzustand und gehoeren deshalb ins Template, nicht in eine Transaktion. - Notiert: gc-transaktion.py findet den Token aus einem Worktree heraus nicht (210-nautobot.yml liegt nur im Haupt-Checkout); die Transaktionsliste kommt trotzdem, nur die Compliance-Spalte fehlt. Konflikt in teilprojekte.md (ROS-7.24.1-Zeile) zusammengefuehrt statt eine Seite zu verwerfen: TP27-Erledigung samt Zeitstempeln aus main plus die npk-Folge fuer TP16.3 aus diesem Branch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2808 lines
267 KiB
Markdown
2808 lines
267 KiB
Markdown
# BF-Netzwerk — Teilprojekte (kanonische Steuerung, Stand 2026-06-10)
|
||
|
||
> **Zweck:** Das große Netz-Projekt in **einzeln durchziehbare Teilprojekte (TP)** zerlegt — jedes mit klarem Scope,
|
||
> aktuellen Entscheidungen, Abhängigkeiten und Status. Jedes TP ist so geschnitten, dass es eine frische Session
|
||
> **mit sauberem Kontext** straight abarbeiten kann. Architektur/Historie: `~/.claude/plans/ich-will-das-delegieren-atomic-sunrise.md`.
|
||
>
|
||
> 📚 **Diese Datei führt den Ist-Stand, den kritischen Pfad und die OFFENEN Teilprojekte.**
|
||
> Abgeschlossene TPs stehen im **[Archiv](teilprojekte-archiv.md)** — im Volltext, unverändert, mit
|
||
> Inhaltsverzeichnis. **Für den Einstieg reicht diese Datei**; ins Archiv sieht man, wenn man wissen
|
||
> will, *warum* etwas so entschieden wurde. *(Aufgeteilt am 2026-08-23, Doku-Session Punkt F: 2217
|
||
> Zeilen waren zu 49 % Historie erledigter TPs.)*
|
||
|
||
## Kanonisches VLAN-Modell (gültig — überschreibt ältere Tabellen)
|
||
| VLAN | Name | Subnetz (Ziel) | Zweck |
|
||
|---|---|---|---|
|
||
| 10 | MAIN | 10.40.10.0/24 | Trusted (Handy/Laptop/Apple Watch) |
|
||
| 20 | INFRA | 10.40.20.0/24 | NAS/VMs/Docker/HA |
|
||
| 30 | IOT | 10.40.30.0/24 | Smart-Home (HA-managed), Waage, Fitness-Tracker |
|
||
| **40** | **MEDIA** | 10.40.40.0/24 | **Sonos, AppleTV, TV, VuPlus, Cast-Ziele** (getauscht ggü. früher) |
|
||
| **50** | **UNTRUSTED** | 10.40.50.0/24 | **Spielekonsole, eBook-Reader, Low-Trust-Internet** |
|
||
| 90 | GUEST | 10.40.90.0/24 | nur Internet |
|
||
| 99 | MGMT | 10.40.99.0/24 | Switch/AP/Hypervisor-Mgmt |
|
||
| 100 | WAN | (Arris-Transfer) | zum Arris-Kabelmodem (Liwest, Bridge-Mode, Public-IP an `crs326-core`) |
|
||
IP-Blöcke: `.1–10` Router/Gateways · `.11–20` Switches/APs. **GW je VLAN = `.1` (crs326-core).**
|
||
Mgmt (VLAN99, Ist 2026-08-16): core `.99.1` · branch `.99.2` · ap1 `.99.11` · ap2 `.99.12`.
|
||
*(Historisch, überholt: „ap1=.1, ap2=.2 · ALLNET=.11, Omada=.12" — aus der Zeit vor dem Core-Cutover.)*
|
||
|
||
## Ist-Stand (Kurz)
|
||
|
||
**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`,
|
||
`VirtualMachine` und `Tag` sind **alle 0**, installiert ist nur `golden-config`. Kein Host, kein
|
||
Container, kein Dienst ist erfasst. **TP37 ist damit zuerst eine Datenmodell-Frage** und liefert das
|
||
Fundament, das **TP34** (Caddyfile via GC) und **TP36** (SSoT für diese Datei) ebenfalls brauchen —
|
||
Reihenfolge: **TP37 vor TP34**.
|
||
- 🆕 **CF-Public-Strang eingedampft (2026-08-30, TP16.3 grosser Schnitt):** **12 Hostnames** aus Ingress
|
||
**und** DNS entfernt — `pve`/`portainer`/`pve-mcp`/`ha-mcp`/`ds-bf`/`ccu`/`iobroker`/`z2m25`/`pbs`
|
||
plus die drei CF-Access-SSH-Ziele. Ingress: `bf-mm` 6→4, `bf-nas` 6→3, `bf-pve` 9→2. **Öffentlich
|
||
bleiben nur `vw`/`hass`/`n8n`/`gitea`** (weiterhin HTTP 200). Intern unverändert über AdGuard→Caddy
|
||
(9× HTTP 200). ✅ **Nachtrag 2026-08-30 (F1 durch): `gitea-mcp`/`n8n-mcp` sind jetzt AUCH
|
||
geschnitten** — damit **14 Hostnames** gesamt und `bf-mm` **4→2** (nur noch `n8n` + Catch-all).
|
||
Möglich wurde das, weil zuvor **12 MCP-Client-Configs** auf LAN-IP migriert wurden (nicht 10 wie
|
||
geschätzt: zwei Worktree-Kopien in `bf-network-r6{,.opencode}` kamen dazu). 🔴 **`bf-public` hat weiterhin 0 Connectoren** — er gehört
|
||
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.
|
||
⚠️ **Korrektur (2026-08-31, nachträglich):** die dabei gesehene `wifi-security`-Drift auf
|
||
`crs326-core` (`sec-main` `ft-over-ds=no` statt `yes`) hatte **nichts mit dem Upgrade zu tun** — sie
|
||
ist **TP25 Fund E, Hebel 1**, um 08:09 bewusst gesetzt (Core-Upgrade war erst 14:36) und als
|
||
**offene Transaktion `TP25-fund-e-ft-over-ds`** geführt (Scope `wifi-security`, Ablauf 2026-09-03).
|
||
**Die 134/135 sind Absicht**, nicht Schaden: grün machen würde den laufenden Versuch unsichtbar
|
||
machen, und `gc-transaktion.py` verweigert den Commit ohnehin, solange der Scope driftet.
|
||
🔑 **Lehre:** vor jeder Drift-Bewertung `nautobot/gc-transaktion.py status` lesen — Drift aus einem
|
||
laufenden Versuch sieht identisch aus wie ein Versehen, und genau dafür gibt es das Werkzeug.
|
||
- 🆕 **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 fünf MikroTik laufen auf RouterOS `7.24.1`** (🆕 **Angleichung am 2026-08-31 von Christoph
|
||
selbst durchgezogen, TP27 damit erledigt** — ap2 08:56, core 14:36, ap1 14:50, branch 14:52;
|
||
ap3 kam bereits so. RouterBOOT jeweils = `upgrade-firmware` = `7.24.1`, live gegengelesen).
|
||
Vorher: vier auf 7.24 (08-17), nur ap3 auf 7.24.1. Neu in der Flotte **`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
|
||
hält die öffentliche Liwest-IP **`90.146.33.177/24`** direkt vom Arris (Bridge-Mode) → kein Double-NAT.
|
||
**DNS-Upstream = DoH zum CF-Gateway mit `verify-doh-cert=yes`** (TP23 Stufe 1 ✅ 08-18);
|
||
`servers=1.1.1.1` steht nur noch als Bootstrap für den DoH-Hostnamen.
|
||
- 🔥 **Die Firewall ist deny-by-default — in BEIDEN Ketten** (TP5 E3/E4 ✅ 08-18). `TP10-seal-lan` ist
|
||
`disabled`; Mgmt-Input kommt nur noch aus VLAN10, von HA (`8728`), von Nautobot (`22`) und über
|
||
`cf-mesh`. Ein neuer Dienst über eine VLAN-Grenze braucht ab jetzt eine explizite Regel.
|
||
- **L2-Branch = `crs326-branch`** (Technik OG): reiner L2-Switch, Mgmt **`10.40.99.2`**,
|
||
Uplink `ether1` → core `ether8`.
|
||
- **WLAN = CAPsMAN auf dem Core** (TP28 ✅ 08-17, GC-Modell nachgezogen 08-18): ap1 **`10.40.99.11`**
|
||
(an core `ether15`), ap2 **`10.40.99.12`** (an branch `ether24`), beide **reine CAPs** —
|
||
`cap enabled=yes`, `discovery-interfaces=vlan99`. Die APs halten **keine eigene WLAN-Config** mehr;
|
||
Kanäle, SSIDs, Security und Steering kommen vom Manager. 802.11r/k/v aktiv, `ft-over-ds=yes`.
|
||
🆕 **Dritter AP `ap3` (cAP ax) ✅ LIVE seit 2026-08-30** (TP28 dafür wieder geöffnet):
|
||
`10.40.99.13`, Uplink `ether1` → **core `ether21`** (Trunk), reiner CAP, **RouterOS 7.24.1**
|
||
(🆕 **seit 2026-08-31 die ganze Flotte** — die frühere Versionsdifferenz ist aufgelöst). Kanäle
|
||
**`5180/ax/Ce/I`** + **`2437/ax`** (ch6) nach dem TP25-Plan vom 2026-08-24, am Gerät nachgemessen.
|
||
🔴 **ap1 ist funkstill** — es steht bis zu seinem Umzug ans Sideboard direkt neben ap3 und würde
|
||
auf 5180 kollidieren; es hat deshalb bewusst **keine Provisioning-Regel** am Manager, bleibt aber
|
||
erreichbar. Beim Umzug: ap1 → `5260/Ce` (Band B, DFS, **Frequenz-Liste statt Einzelwert**),
|
||
ap2 → `5300/Ce`, Uplink ap1 → `core ether3`.
|
||
**Compliance jetzt 130/130 / 0 Drifts über 5 Geräte** (vorher 104/104 über 4).
|
||
- **SSDP-Relay** (TP7.1, **nachgebessert 08-25**): LXC **108 `mcast-relay`** auf pve, zweibeinig
|
||
`10.40.10.21` (VLAN10) + `10.40.40.21` (VLAN40) — dafür trägt `core ether13` beide VLANs; mDNS macht
|
||
weiterhin der Core. **Läuft seit 08-25 ohne `--ssdpUnicastAddr`** — die Flagge verwarf die M-SEARCH
|
||
der App auf dem Weg nach VLAN40 (Messung: **0 Pakete `via eth1` gegen 308 `via eth0`** in 20 min,
|
||
**0×** `Received SSDP Unicast` im ganzen Journal seit 08-18). Der Rückweg läuft jetzt **nativ geroutet**
|
||
über die längst offene Regel `TP5: MEDIA->MAIN`; nachgemessen kommen `HTTP/1.1 200 OK` von
|
||
`10.40.40.193/.195` an beiden MacBook-Beinen an. Unit liegt im Repo: **`mcast-relay/multicast-relay.service`**,
|
||
Details als Gotcha in `CLAUDE.md`. ⚠️ **Der Fall hatte zwei unabhängige Ursachen** — nach der
|
||
Relay-Reparatur suchte die **App** immer noch nicht (macOS-Local-Network-Gate stand auf „an" und
|
||
griff nicht; Fix war Schalter aus/ein + Neustart, User-bestätigt 08-25). **Beide sind erledigt.**
|
||
- **Reverse Proxy = `caddy-bf1`** (LXC 130 auf pve, **`10.40.20.40`**, TP31 ✅ Schritt 1–3 seit
|
||
2026-08-25, **`hass` 400 behoben 2026-08-26, Split-Horizon seit 2026-08-26**): Caddy 2.11.4,
|
||
**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** (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
|
||
mit `.bf.`-Suffix + alte suffixlose parallel). DHCP liefert `.36`/`.37` als DNS in allen 7 VLANs
|
||
(`domain=emsenhuber.net`), Firewall forward UDP+TCP/53 auf beide. Der Core-Resolver (`10.40.X.1`)
|
||
bleibt als Bootstrap unverändert (DoH, `allow-remote-requests=yes`, `mdns-repeat-ifaces`).
|
||
- **Ausgebaut / gelöscht:** ALLNET-`sw1` und Omada-`sw2` sind **physisch weg** (2026-08-08 bzw. 2026-07-23,
|
||
in Nautobot *Decommissioned*); Omada-Controller + `omada-mcp` gelöscht (2026-07-16); FritzBox 6660 raus
|
||
seit 2026-07-01 (Liwest-Tausch auf Arris). **VLAN1 ist leer** (`/ip arp` auf `bridge` = 0 Einträge,
|
||
08-18) — TP19 ist geschlossen; `cudy1` hängt stromlos an core `ether11`, physischer Ausbau ist hands-on.
|
||
- 🆕 **WLAN-Logging persistent seit 2026-08-30 (TP25 Fund E):** `wireless` + `caps` gehen vom Core
|
||
in den TP5-Kollektor (`10.40.20.18:514`); `/system logging` steht unter der **27. ComplianceRule**
|
||
`system-logging`. Vorher nur Ringpuffer → ein seit Wochen gemeldeter iPhone-Abbruch war rückwirkend
|
||
nicht diagnostizierbar.
|
||
- 🔴 **TP25 Fund E — Hebel 1 ist GESCHEITERT (Stand 2026-08-31 nachmittags):** `sec-main` steht am
|
||
Core auf **`ft=yes ft-over-ds=no`** (seit 08:09), aber das Symptom blieb: **124× `reauthenticating`
|
||
nach dem Eingriff** und **zwei von Christoph erlebte Ausfälle** — 13:16 (Videocall bricht ab,
|
||
7 min ohne WLAN) und 14:23 (5,5 min, nur durch WLAN-Toggle beendet). Stundenvergleich auf
|
||
gleicher Firmware: **07:00–08:00 = 79 · 14:00–15:00 = 70**. Erfolgskriterium war „~0" ⇒ nicht
|
||
eingetreten. Gewertet wird bewusst nur **08:09–14:36** (danach kommt das TP27-Upgrade als zweite
|
||
Variable dazu; Christoph-Entscheid: „Vormittag als Ergebnis werten").
|
||
⇒ Transaktion **`TP25-fund-e-ft-over-ds` bleibt OFFEN** (Ablauf **2026-09-03**), **Compliance
|
||
weiter bewusst 134/135**; Rollback oder Commit ist eine Entscheidung, kein Aufräumen.
|
||
Geräte-Backup `pre-tp25-funde-ftoverds.backup` liegt auf dem Core.
|
||
**Nächste Entscheidung:** Hebel 2 (`ft=no`) **oder** `authentication-types=wpa2-psk,wpa3-psk` —
|
||
`sec-main` ist auch die einzige SSID ohne `wpa2-psk`, und `association rejected, no cached PMK`
|
||
(157× am 31.08., 0× am 30.08.) ist eine SAE/PMKSA-Meldung. **Vorher prüfen, ob 7.24.1 auf dem
|
||
Manager das Verhalten schon verändert hat** — zwischen 14:37 und 15:12 stand nur **1**
|
||
`reauthenticating` im Log, keines vom iPhone (35 min = noch keine Aussage).
|
||
Details: [`handoff-20260831-tp27-upgrade-und-hebel1-befund.md`](handoff-20260831-tp27-upgrade-und-hebel1-befund.md).
|
||
- ⚠️ **Messfalle, die diese Session fast erwischt hat:** die 13 CAP-`joined`/`activity timeout` vom
|
||
31.08. sahen nach CAPWAP-Instabilität aus — es waren die **Reboots des TP27-Upgrades**. Und die
|
||
**`cap-wifiN`-Karte hat sich an einem Tag zweimal verschoben** (08:09, 14:36), womit die im
|
||
Vorgänger-Handoff festgehaltene Tabelle nach sechs Stunden falsch war. Interface-Namen in
|
||
Logzeilen sind ohne zeitnahen Karten-Snapshot **nicht zuschreibbar**.
|
||
- **Kontrolle / Governance:** MikroMCP für Reads/Debug (alle 5 Geräte in der Registry),
|
||
**Config-Änderungen ausschließlich über Nautobot Golden Config** (TP21/TP22 — Deploy-Pipeline scharf,
|
||
**135/135 Compliance-Checks grün, 0 Drifts** bei **27 Rules × 5 Geräten** (Stand 2026-08-30;
|
||
**seit 2026-08-31 vorübergehend 134/135 — der eine Drift ist die offene Transaktion
|
||
`TP25-fund-e-ft-over-ds`, s. oben, kein Fehler**) — live gegen die API
|
||
geprüft am **2026-08-23**; zuletzt dazugekommen: **`wifi-access-list`** (08-22), **`queue`**
|
||
(08-23, TP6) und **`ip-firewall-mangle`** (08-23, TP6.1), s. TP22-Nachtrag im Archiv).
|
||
*(Hier stand bis zum 08-23 „100/100 bei 25 Rules" — eine Zählung, die TP6.1 am selben Tag überholt
|
||
hat. Die Merkregel gilt: der Ist-Stand-Block wird bei jeder Änderung mitgezogen, nicht nur der TP.)*
|
||
|
||
<details><summary>📜 Historische Fassung (Stand bis 2026-08-08, Topologie <b>vor</b> dem Core-Cutover — bewusst erhalten, nicht als Ist lesen)</summary>
|
||
|
||
> ALLNET-Core `.11` (FW2.2.1, VLANs 10/20/30/40/50/90/99/100; GE1/GE2/GE8 Trunk; **TP1 ✅ 2026-06-15**). Omada-Switch `.12` (adopted, **TP2 ✅ 2026-06-15**; Trunk-ALL auf P1+P2, Access-Profile auf P3-P8; Profile noch PVID=1 bis Omada-PVID-Fix).
|
||
> Omada-Controller **`.175`** (DS923/macvlan, mbentley 6.2, 🟢 live). omada-mcp **`.176`** (pve-LXC, StreamableHTTP, 🟢 live, 84 read-Tools; Controller-API v2 intern via christoph@emsenhuber.com).
|
||
> 2× hAP ax² (7.23.1): ap1 `.1` (ALLNET P1) — **TP4 ✅ 2026-07-01: Router + WAN-Edge** (VLAN-Subinterfaces, DHCP, DNS, NAT, Public-Edge). ap2 `.2` (Omada P2 — Trunk).
|
||
> **Edge-Cutover ✅ 2026-07-01:** FB6660 physisch entfernt (Liwest-Kabelmodemtausch, kein Zurück), Arris-Kabelmodem im Bridge-Mode; WAN = `sw1 gi16 Access VLAN100 → gi15 Trunk → ap1 ether2 → vlan100 auf bridge` (**NICHT** ether1); ap1 hält Public-IP direkt (Liwest, kein CGNAT), Double-NAT weg. Telefonie war auf der FB und wird nicht weitergeführt. Vorarbeit: TP10-Kern durch (2026-06-27, WG-Hub live) → Bridge-Gate vorher gelöst. Runbook: `routeros/wan-cutover-runbook.md`.
|
||
> Kontrolle: MikroMCP (MikroTik) live · ALLNET = Klickanleitungen · Omada → Controller + omada-mcp 🟢.
|
||
|
||
</details>
|
||
|
||
**Nächster Schritt (Stand 2026-08-30): Vor-Ort-Session ap1/ap2** — sie ist der einzige Weg, den
|
||
Public-Strang auf `bf-public` zu heben und die drei Alt-Tunnel samt sechs Containern abzubauen
|
||
(TP16.3 Follow-up F2). ✅ **F1 ist am 2026-08-30 durch** (12 Client-Configs auf LAN-IP, danach
|
||
`gitea-mcp`/`n8n-mcp` aus Ingress+DNS geschnitten) — davor unabhängig machbar bleibt nur noch die
|
||
**Announcement-Doku (TP35 Schicht 4)**, die den anderen Projekten die neuen MCP-URLs mitteilt.
|
||
|
||
**Vorherige Fassung (Stand 2026-08-28): drei Folge-Sessions parallel** (siehe `docs/handoff-20260828-mcp-bestand-und-performance.md`):
|
||
> - ✅ **Session A** — `/projektstatus`-Skill global für opencode (TP35 Schicht 6). Funktionsbeweis erbracht 2026-08-28.
|
||
> - 🔜 **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
|
||
> von **sechs Containern auf einen** ein; [TP32](#tp32) gibt die Nummern- und Adress-Systematik vor
|
||
> (und hat nebenbei die **falsche Startreihenfolge auf pve repariert** — HA startete vor seiner
|
||
> Datenbank). **TP16 ✅, TP25 ✅ und TP16.3 (Pilot) ✅** sind am 24./25. durchgezogen worden.
|
||
|
||
**Vorherige Fassung (Stand 2026-08-23): Christoph entscheidet — der kritische Pfad ist LEER.**
|
||
> 🎯 **Das Projekt ist in seinem Kern fertig.** deny-by-default steht (TP5), Golden Config steht und
|
||
> meldet **104/104 compliant bei 0 Drifts** (live geprüft 2026-08-23), WLAN steht über CAPsMAN auf dem
|
||
> Core (TP28), VLAN1 ist leer (TP19) — und **TP6 ist beantwortet statt offengelassen**: CAKE gebaut,
|
||
> gemessen, wieder ausgebaut. **Nichts blockiert mehr etwas.**
|
||
> 🟢 **Was zuletzt wirklich etwas gebracht hat, war TP6.1** (2026-08-23): nicht Shapen, sondern
|
||
> **Klassentrennung** — NAS-Bulk `priority=8` gegen interaktiv `priority=1` drückt dieselbe Strecke
|
||
> unter derselben Sättigung von **40,8 auf 11,0 ms (−73 %)**. Die zuerst notierte DOCSIS-Erklärung aus
|
||
> TP6 ist damit **widerlegt**; der Fehler lag in der Messmethode (Last und Messung vom selben Host).
|
||
> 📋 **Offen und ohne Blockadewirkung:** **TP25** (Fund F = 5:05-Takt bei IoT, plus die Entscheidung
|
||
> „5 GHz auf 20 MHz") · **TP16** (CF-Tunnel-HA degradiert, vier tote Alt-Tunnel) · **TP16.2/16.3** ·
|
||
> **TP23 Stufe 2** · **TP24** (DC-USV) · **TP13/TP14.4** (beide mit fertigem Runbook) ·
|
||
> ❌ **TP17.1 VERWORFEN 2026-08-23** (Zigbee-Weg bewiesen, Ziel damit nicht erreichbar — das Gerät
|
||
> bleibt WLAN-Client; `s9` zurückgebaut) · ✅ **TP17.3 ERLEDIGT** (Ursache **`eco_mode`** bewiesen;
|
||
> `s23` + `s26` repariert; **`eco_mode` flottenweit aus**, 22/22 außer `s24`) ·
|
||
> ➡️ **TP17.2 ist am 2026-08-24 in TP30 aufgegangen** (Shelly-Gerätepasswort = Erstvergabe eines
|
||
> Credentials; TP30 ist das Sammelbecken dafür) ·
|
||
> 🔑 **Alle Passwort-/Token-Rotationen sind seit 2026-08-24 TP30** (Sammel-TP) — **bewusst
|
||
> gebündelt gegen Projektende**, User-Entscheid. Damit ist die ältere Einreihung „hinter TP17.1"
|
||
> gegenstandslos (TP17.1 ist verworfen). **Etappe 1 — die Service-Accounts — ist durch;**
|
||
> das `admin`-Passwort selbst ist **nicht rotiert** und bleibt es bis dahin bewusst.
|
||
> 📌 **Einstieg für die nächste Session:
|
||
> [`handoff-20260825-agh-dns-cutover.md`](handoff-20260825-agh-dns-cutover.md)** — AdGuard als
|
||
> Resolver + Split-Horizon (TP23 Stufe 2 ∥ TP31 Schritt 4). **Bewusst eigene Session:** der Schritt
|
||
> verschiebt die DNS-Auflösung des ganzen Standorts in einen Container.
|
||
> 📜 Älter: [`handoff-20260824-session.md`](handoff-20260824-session.md)
|
||
> — ⚠️ dessen Empfehlung „Start bei der Passwort-Rotation Etappe 2" ist durch den TP30-Entscheid
|
||
> **überholt**; der Rest der Übergabe gilt. Ältere Sitzungsvorschläge:
|
||
> [`handoff-20260822-naechste-schritte.md`](handoff-20260822-naechste-schritte.md).
|
||
> ✅ **`crs326-core` ist transaktionsfrei** (seit 2026-08-22, geprüft): ein gemeldeter Drift ist ab
|
||
> jetzt **echt**, nicht beabsichtigt — `python3 nautobot/gc-transaktion.py status`.
|
||
|
||
<details><summary>📜 Vorherige Fassungen dieses Absatzes (2026-07-22 → 2026-08-21, bewusst erhalten)</summary>
|
||
|
||
> *Fassung 2026-08-21 abends (erledigt — TP25s Dead Spot ist am 08-22 repariert, TP6 am 08-23 geschlossen):*
|
||
> **Nächster Schritt: TP25 — Steering-Test in den Schlafräumen.**
|
||
> 📋 **Einstieg: [`handoff-tp25-session-20260821.md`](handoff-tp25-session-20260821.md)** — dort steht
|
||
> der verifizierte Ist-Stand, die Messfallen und der nächste Test.
|
||
> ✅ **`crs326-core` ist transaktionsfrei (2026-08-22).** `TEST-ether3-trunk-ap1-sideboard` und
|
||
> `TEST-infra-ssid-matter-pairing` sind beide zurückgerollt und geschlossen; ap1 hängt wieder auf
|
||
> `ether15`, `ether3` ist wieder untagged VLAN40. Ein gemeldeter Drift ist ab jetzt **echt**, nicht
|
||
> beabsichtigt — `python3 nautobot/gc-transaktion.py status`.
|
||
> 🆕 **TP29** (Anwesenheitserkennung fürs Energiesparen) ist als Idee angelegt, nicht auf dem Pfad.
|
||
> 📌 **Die TP6-Prämisse ist am 2026-08-20/21 gemessen** (Runbook §5): formal Fall B (+28…+47 ms unter
|
||
> Dauer-Upload), aber im Videocall **nicht wahrnehmbar** — kein einziger Problem-Klick über sechs
|
||
> Lastphasen. Laut war stattdessen der Funk: **Sticky Client + Band-Ping-Pong, zwölf Wechsel in neun
|
||
> Minuten**, je Wechsel bis zu 4 s ohne Durchsatz →
|
||
> [`tp25-wlan-roaming-messung-20260820.md`](tp25-wlan-roaming-messung-20260820.md). Deshalb steht jetzt
|
||
> **TP25 vor TP6**; TP6 bleibt offen und begründet, aber nachrangig.
|
||
>
|
||
> *Vorherige Fassung (Stand 2026-08-18, erledigt):*
|
||
TP6 wird hier seit Monaten als „DAS Ziel" geführt, aber `docs/call-quality-diagnosis.md` hat den
|
||
Bufferbloat 2026-06-16 bereits gemessen (**A/A+**, „CAKE wäre marginal, nicht deployt"). Die Messung
|
||
stammt aus der FritzBox-Ära, der Edge ist seither getauscht — also **einmal gegen den heutigen Edge
|
||
nachmessen und dann entscheiden**. Blocker gibt es keine mehr. TP5 ist in der Nacht auf den 18. abgeschlossen worden (Etappe 3 + 4: deny-by-default in `forward` **und** `input`, MGMT isoliert, `seal-lan` abgeschaltet), TP23 Stufe 1 (DoH auf dem Core) ebenfalls. Nebenher offen, blockiert nichts: **TP25** (Signal-Boden + Re-Walk), **TP28-Rest** (die fünf `wifi-*`-Features vom AP-Scope auf den Core umhängen — die einzigen verbliebenen Compliance-Drifts), **TP7.1** (SSDP-Relay für die macOS-Sonos-App), **TP16.3**, **TP24**.
|
||
> *Vorherige Fassung (Stand 2026-08-14, überholt): „TP5 — Deny-by-default am Ende von `forward`." Alles Davorliegende ist durch:* CRS326-Bring-up + L3-Umzug ap1→`crs326-core` (TP15/TP21.1 ✅ 2026-08-08), WLAN-Neuprovisionierung ohne CAPsMAN (TP3 ✅ 2026-08-08), Nautobot-Plattform + Fleet-Adoption + Deploy-Pipeline (TP21/TP22 ✅ 2026-08-13). Danach **TP6 (CAKE) = das Projektziel**. Kleine Follow-ups nebenher: DoH-Cloudflare auf core, DC-USV (**TP24**, seit 2026-08-16 eigener TP). ⚪ **Omada-PVID-Fix ist obsolet** (User-Entscheid 2026-07-22 — Omada spielt seit der TP15-Korrektur keine Rolle mehr im Netz, nie mehr aufgreifen).
|
||
> *Vorherige Fassung (Stand 2026-07-22, erledigt): „TP15 CRS326-Bring-up als neuer L3-Core, danach WLAN-Neuprovisionierung ohne CAPsMAN; TP5 folgt nach TP21/CRS326-Adoption."*
|
||
|
||
</details>
|
||
|
||
> 🎯 **Ziel: sauberes System „from scratch", keine Leichen/Altlasten.** Migrations-Session-Endstand = **VLAN1 + altes cudy-WLAN komplett weg** (VLAN1 nur noch Mgmt, `defconf`-Client-DHCP zurückgezogen, cudy-Uplinks aufs Ziel-Client-VLAN getaggt oder cudy abgebaut). **Unterpunkte/Aufräum-Liste bei Migration:** (a) die Interim-Härtung ap2 `wifi1/wifi2 frame-types=admit-only-vlan-tagged` (TP3.1, 2026-07-06) bleibt gültig — verschwindet automatisch mit VLAN1; (b) ~~`st-main rrm/wnm` = reines Roaming-Provisorium~~ → **kein Provisorium mehr (2026-08-13, TP3.3): Steering ist fester Bestandteil des Golden-Config-Templates + ComplianceFeature `wifi-steering`** — es war die Neuprovisionierung, die es stillschweigend verloren hatte; (c) ~~**cudy1 = verbleibender aktiver VLAN1-Leak**~~ → **ERLEDIGT (User-bestätigt + verifiziert 2026-08-14): cudy1 ist offline.** `core ether11` trägt **kein Link** (kein `R`-Flag), Nautobot-Status *Offline* (TP22 Schritt 5). Damit existiert kein aktiver VLAN1-Leak mehr; der Dual-DHCP-Race aus `project_vlan1_dhcp_incident` ist Historie. Der formale Teardown (Gerät ausbauen, Doku/Inventar) bleibt TP19.
|
||
> ⚠️ **HW-Constraint hAP ax²:** 1 GB RAM, aber nur **128 MB Flash + KEIN USB** → **Container auf dem Router NICHT praktikabel**.
|
||
> Folge: **native RouterOS-Features bevorzugen** (z.B. Built-in-mDNS statt Container); Container (mDNS-Reflector, BIND9-DNS, Omada-Controller) laufen auf **NAS/pve**, nicht am hAP.
|
||
> 🔒 **Governance-Entscheid (User, 2026-07-16, ausgeweitet 2026-07-16): Config ausschließlich über Nautobot — keine Direkt-Changes mehr an einzelnen im Netz verstreuten Geräten.** Ursprünglich als ap1/ap2-Config-Freeze bis TP21 formuliert, noch am selben Tag verallgemeinert: **jede** Netz-Gerätekonfiguration (Router, APs, künftig auch die CRS326-Switches) läuft ab jetzt exklusiv über Nautobot Golden Config (TP21), sobald das steht — keine ad-hoc MikroMCP-Writes/UI-Klicks mehr an Einzelgeräten. **TP19.1 war die letzte Ausnahme** (ap1-DNS-Fix + ap2/sw1/sw2-Mgmt-VLAN-Umzug, s.u.) — davon ist nur der risikolose ap1-Teil noch direkt gemacht worden, ap2 wurde bewusst auf „nach Nautobot" vertagt. **Macht Nautobot + alle Prerequisites (TP18→TP20→TP20.1→TP21, jetzt auch Komodo) zum aktuell kritischen Pfad**, vor TP5/TP6. Betrifft nicht Software-Migrationen auf NAS/pve (anderes Substrat) und die Frage, ob sw1/sw2 vor der CRS326-Ablösung noch angefasst werden (offen, s. TP15/TP19.1).
|
||
> ✅ **Genehmigte Ausnahme (User, 2026-07-17): der zentrale CF-Tunnel-Zugang auf ap1 (TP16/TP16.1).** Alle für den zentralen Cloudflare-Zugang **auf ap1** notwendigen (Vor-)Arbeiten dürfen direkt via MikroMCP/CF-API gemacht werden, ohne auf Nautobot GC zu warten — konkret die nativen WireGuard-Mesh-Zeilen aus TP16.1 (und analoge cloudflared/WG-Anpassungen am ap1-Edge). Begründung: Remote-Erreichbarkeit ist die Voraussetzung, um überhaupt sicher am Netz zu arbeiten (Henne-Ei zu Nautobot). **Scope-Grenze:** Die Ausnahme gilt **nur für ap1**. Sobald der zentrale Zugang später auf die **CRS326** wandert (TP15), gilt die Ausnahme dafür **vorerst NICHT** — CRS326-Config läuft dann regulär über Nautobot GC (oder braucht eine eigene, dann neu zu erteilende Ausnahme). Nachtrag der ap1-Ausnahmezeilen in die GC als Follow-up, sobald Nautobot steht.
|
||
> ✅ **Genehmigte Ausnahme #4 (User, 2026-08-16): die WireGuard-Zeilen des Cloudflare-Mesh-Bring-ups auf `crs326-core` (TP16.1).** Dürfen direkt via MikroMCP/REST gesetzt werden. **Begründung ist strukturell, nicht bequem:** TP22-Regel 1 schließt jede Sektion mit `<<REDACTED>>` generell vom GC-Push aus, und `/interface wireguard` ist genau so eine — ein privater Schlüssel ist über Golden Config **nicht deploybar**. Damit ist die frühere TP16.1-Zusage „CRS326-Zeilen laufen regulär über GC" gegenstandslos. **Auflage:** vorher `/system backup`, danach Ist-Stand als intended config zurück nach Nautobot. **Scope-Grenze:** nur der Mesh-Bring-up auf `crs326-core` — nicht spätere WG-Änderungen, nicht andere Geräte, und sie verlängert sich nicht automatisch. Langfristige Auflösung = das Secret-Handling-Design in TP16.1 (Privkey ist Geräte-Identität, nicht Config).
|
||
> ✅ **Genehmigte Ausnahme #2 (User, 2026-07-22): der CRS326-Bring-up (TP15).** Die **Grundkonfiguration** beider CRS326 — Management-IP, Bridge, VLAN-Filtering, Trunks/Access-Ports — darf **direkt** via MikroMCP/Winbox gemacht werden. **Begründung = Henne-Ei:** Golden Config kann nicht auf ein Gerät pushen, das im Werkszustand keine erreichbare IP hat, und die GC-Templates existieren noch nicht (TP21 „offen: Templates/intended config"). **Auflage:** der so entstandene Ist-Stand wird anschließend als **intended config nach Nautobot zurückgeschrieben**, danach übernimmt GC — die Ausnahme deckt das Bootstrapping, nicht den Dauerbetrieb. **Scope-Grenze:** gilt für die beiden CRS326 im Bring-up. Sie hebt die Regel für ap1/ap2 **nicht** auf (deren Neuprovisionierung läuft weiterhin über GC) und verlängert sich nicht automatisch auf spätere CRS326-Änderungen. Damit ist die frühere Formulierung in TP16.1 („CRS326-Config läuft regulär über Nautobot GC oder braucht eine eigene, dann neu zu erteilende Ausnahme") **erfüllt** — dies ist diese Ausnahme.
|
||
|
||
---
|
||
|
||
## Offene Teilprojekte
|
||
|
||
> **Alles hier hat noch offene Arbeit.** Abgeschlossenes steht im
|
||
> **[Archiv](teilprojekte-archiv.md)** (Volltext, mit Inhaltsverzeichnis) — die Zuordnung ist
|
||
> mechanisch: *ein TP wandert erst dann ins Archiv, wenn nichts mehr daran zu tun ist* — **und der
|
||
> Weg zurück ist offen: ein archiviertes TP ist ein geschlossenes Ticket, kein versiegeltes.**
|
||
> Kommt an derselben Baustelle neue Arbeit dazu, **wandert der Volltext zurück hierher** (Zeiger
|
||
> bleibt im Archiv stehen, damit alte Links halten) — statt ein Zwillings-TP mit neuer Nummer
|
||
> anzulegen. *(Christoph-Direktive 2026-08-30, erster Fall: **TP28** wegen `ap3`.)* Wo nur ein
|
||
> Sub-TP offen war, steht dieses hier und das erledigte Eltern-TP im Archiv (so bei
|
||
> ~~**TP17.2** ↔ TP17~~ *(TP17.2 ist am 2026-08-24 in **TP30** aufgegangen und kein eigenes TP mehr)*
|
||
> und **TP20.1** ↔ TP20).
|
||
>
|
||
> ### 📊 Quick-Index (nur dieser Block ist für den Statusbaum nˆtig — 1 Read)
|
||
>
|
||
> | # | Status | Kurztitel | Abschluss |
|
||
> |---|---|---|---|
|
||
> | TP5.1 | ⏸️ | DNS-Visibility Airfryer | — |
|
||
> | TP8 | ⏸️ | Identity / 802.1X | — |
|
||
> | TP11 | ⏸️ | Monitoring | — |
|
||
> | TP12 | ⏸️ | IPv6 (3 Standorte) | — |
|
||
> | TP12.1 | 🔜 | IPv6 ULA + v6-Firewall | — |
|
||
> | TP13 | 🔜 | Botschafts-WLANs | — |
|
||
> | 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; §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 | — |
|
||
> | TP20.1 | 🔜 | Vaultwarden → Platform-PG | — |
|
||
> | TP23 | ✅ | DNS-Policy (Stufe 1+2 ✅) | 2026-08-26 |
|
||
> | TP24 | 🔜 | DC-USV | — |
|
||
> | TP25 | 🔬 | WLAN-Stabilisierung (Kern ✅) — **Fund E offen, Hebel 1 `ft-over-ds=no` läuft, Messung bis 09-01** | 2026-08-31 |
|
||
> | 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 (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
|
||
- **Kontext (2026-07-08/09):** Airfryer „airschreier" (Philips/Versuni, ESP, `10.40.30.180`) bekam per DHCP-Option 6 `dns 1.1.1.1` als Override, weil das OTA-Update über den ap1-DoH-Gateway-Pfad nicht durchging (DoH-Latenz beim primitiven ESP-Stack — **kein** Content-Block, via Gateway-Logs bewiesen, s. `memory/ref_esp_ota_doh_not_block`). Nebenwirkung: public **1.1.1.1 = anonymer Consumer-Resolver ≠ accountgebundener Gateway-DoH** (`px35ynlol3.cloudflare-gateway.com`) → **DNS-Sicht auf das Gerät weg**, Gerät umgeht zudem die Filterung.
|
||
- **(a) DNS-Redirect statt Geräte-Override** (der dauerhafte Fix): dst-nat „UDP/53 (+TCP/53) von Low-Trust-Src → auf einen *plain* Resolver umbiegen" gibt **Logging zurück UND** funktionierendes OTA (kein langsamer DoH-Pfad fürs Gerät). Redirect-Ziel-Optionen: (i) ap1-Resolver mit *nicht*-DoH-Upstream für dieses Src-Set, (ii) direkt public 1.1.1.1 (dann nur Flow-Logging), (iii) eigener plain-Resolver. Sauberer als „Gerät verschwindet auf 1.1.1.1". Verortung: Teil des UNTRUSTED-Internet-only-Pinnings (TP5-„Offen"); Entscheidung v50-DNS-Policy (gefiltert vs. plain) hängt dran.
|
||
- **(b) Live-Torch als Ad-hoc-Observability:** DNS ist bei direktem 1.1.1.1 blind, **Flows nicht** (NATen über ap1). `torch` auf `vlan30`/`vlan50` gefiltert auf die Geräte-IP zeigt Ziel-IPs live (Versuni → AWS `eu-west-1` IoT erkennbar). Voraussetzung: Gerät aktiv. Merkregel: Airfryer nur online wenn er kocht → Capture ans Einschalten koppeln.
|
||
- **Abh.:** UNTRUSTED-Move des Airfryers (Kandidat, s. VLAN50) + TP5-Deny-by-default. Kein Blocker.
|
||
|
||
### TP8 — Identity & Access-Control ▸ research-gated
|
||
- **RADIUS = FreeRADIUS auf der NAS** (✏️ User-Manager v5 kann **KEIN LDAP** → openldap nur via FreeRADIUS anbindbar).
|
||
- **EAP-TLS (Zert) je Gerät + dynamische VLAN-Zuweisung** (RADIUS-Attribut `Tunnel-Private-Group-ID`). **Einfachste Heim-Alternative:** pro VLAN eine SSID mit **WPA3-PSK** (kein RADIUS) — Abwägung Komfort vs. Admin-Aufwand.
|
||
- Abh.: TP3. (Research erledigt, s. TP-R.)
|
||
|
||
### TP11 — Monitoring & Mgmt-Control-Plane ▸ research-gated
|
||
- MikroMCP + omada-mcp + **SNMP/icinga-mikrotik**, RoMON, NTP, **E-Mail-Alerts**, Netwatch. → Teil von `docs/research/routeros-capabilities.md`.
|
||
- **CF-Tunnel #1033 Track A ✅ live (2026-06-30):** Round-Robin-1033er über 3 asymmetrische Connectors behoben
|
||
durch Reduktion auf pve-only Connector (NAS + macmini scheiterten an pve-lokalen/stalen Origins). SSoT:
|
||
`docs/cf-tunnel-mapping.md`. **Track B/C läuft über TP14** (Ingress bereits v116 .41→.20 für NAS;
|
||
pve-Connector→VLAN20 via TP14 Session B = #1033-Zielbild).
|
||
|
||
### TP12 — IPv6 (intern + extern, 3 Standorte) ▸ research-gated
|
||
- Konzept für FM/BF/DW. → TP-R.
|
||
|
||
#### TP12.1 — IPv6 im LAN (ULA) + v6-Firewall ▸ 🟠 **KONKRET GEWORDEN (2026-08-22), Treiber: Matter/Thread**
|
||
> **Anlass:** der runde IKEA-Fühler **TIMMERFLOTTE** (Matter over Thread) ließ sich nicht anlernen —
|
||
> „pairing failed" in der Companion-App. Ursache ist **nicht** die Firewall, sondern dass zwischen
|
||
> VLAN10 und VLAN20 **überhaupt kein IPv6 geroutet wird**. Damit ist TP12 vom Konzept-TP zu einem
|
||
> Punkt mit einem konkreten, wiederkehrenden Schaden geworden.
|
||
|
||
- **Befund am `crs326-core` (live gelesen 2026-08-22):**
|
||
- IPv6-Adressen: **ausschließlich Link-Local** (`fe80::…`) auf jedem Interface. Keine ULA, keine GUA.
|
||
- IPv6-Routing-Tabelle: nur `::1/128` und `fe80::/64` je Interface. **Keine Route** auf den
|
||
Thread-OMR-Prefix `fd92:505a:c64e:1::/64` und keine auf `fd03:cace:953f:e13d::/64` (den On-Link-Prefix,
|
||
den der OTBR auf VLAN20 wirbt).
|
||
- `/ipv6 nd`: Default-Eintrag, `advertise-dns=no` — es gibt aber ohnehin keine Prefixe zu werben.
|
||
- 🔴🔴 **Der eigentliche Kostenpunkt ist nicht IPv6, sondern die Firewall:** `/ipv6 firewall filter` ist
|
||
**komplett leer**. Heute harmlos, weil ohne Adressen nichts geroutet wird. In der Sekunde, in der
|
||
Adressen vergeben werden, steht **IOT→MAIN offen, GUEST→INFRA offen — an TP5 vorbei**. Das gesamte
|
||
TP5-Regelwerk muss **vorher** in v6 nachgebaut sein, sonst reißt dieser TP genau die Segmentierung
|
||
auf, die TP5 am 2026-08-18 geschlossen hat. **Das ist die Arbeit, nicht die Adressvergabe.**
|
||
- 🔎 **Warum Matter das braucht:** Commissioning läuft in zwei Etappen. Etappe 1 (BLE zum Gerät) ist
|
||
netzunabhängig. In Etappe 2 muss der Commissioner — das iPhone — das Gerät auf dem **operativen
|
||
Thread-Netz** wiederfinden (DNS-SD `_matter._tcp`, dann CASE). Dafür braucht das Telefon eine
|
||
IPv6-Route in den OMR-Prefix hinein. iPhone hängt auf VLAN10, OTBR auf VLAN20 → Etappe 2 bricht ab.
|
||
Die ~30 bereits laufenden Matter-Knoten widersprechen dem nicht: HA und OTBR sitzen auf **demselben
|
||
Host** (`wpan0` ist lokal), im Betrieb quert nichts die VLAN-Grenze. Nur das *Anlernen* tut es.
|
||
- ⚠️ **Zwei Fallen, die den naiven Weg verbauen:**
|
||
- **(a) Der OMR-Prefix ist generiert und wandert.** Am 2026-08-22 um 17:33 (OTBR-Neustart) sind alle
|
||
Knoten von `fd92:505a:c64e:1::/64` auf **Mesh-Local** `fd66:986:170:bb1a::/64` gefallen — und
|
||
Mesh-Local ist per Definition *nicht* off-mesh routbar. Eine statische Route auf den OMR-Prefix ist
|
||
also nur so stabil wie der nächste Neustart. Ihn im Thread-Dataset festzunageln formiert das Netz
|
||
neu — mit 30+ produktiven Knoten daran kein Nebenbei-Change.
|
||
- **(b) RouterOS lernt fremde RAs nicht.** Der OTBR wirbt den OMR-Prefix per RA mit Route-Information-Option
|
||
auf VLAN20. Der Core müsste die annehmen — als forwardender Router für dieses Interface tut er das
|
||
nicht sauber.
|
||
- 📡 **Dritter Baustein: DNS-SD über die VLAN-Grenze.** `mdns-repeat-ifaces` am Core (vlan10/20/30/40/90)
|
||
ist **IPv4**. Für Matter-Discovery bräuchte es zusätzlich einen v6-fähigen Reflector — der
|
||
`mcast-relay`-LXC (108) kann das prinzipiell, aber als **zweite Instanz** mit Beinen in VLAN10+VLAN20
|
||
(SSDP-Instanz ist VLAN10+VLAN40, s. Gotcha in CLAUDE.md).
|
||
- ✅ **Der Workaround, der das umgeht statt löst:** iPhone zum Anlernen auf **VLAN20** — dann sind Telefon
|
||
und Border Router auf demselben L2-Link, der OTBR wirbt dort On-Link-Prefix *und* Route, und iOS wertet
|
||
beides aus. `cfg-infra` / SSID `dirtyvader-infra` (`dp-infra`, VLAN20, `sec-infra` mit PSK) existiert am
|
||
Manager **bereits**, ist nur in **keiner** `provisioning`-Zeile als Slave hinterlegt. Runbook:
|
||
[`docs/matter-pairing-infra-ssid-runbook.md`](matter-pairing-infra-ssid-runbook.md).
|
||
- ✅ **2026-08-22 durchgefuehrt und wieder zurueckgebaut.** `dirtyvader-infra` temporaer auf ap2
|
||
(Transaktion `TEST-infra-ssid-matter-pairing`, Scope `wifi-provisioning`), iPhone dort hinein,
|
||
**TIMMERFLOTTE per Companion-App angelernt** — liefert 25,44 °C / 53,1 % rF / Batterie 100 %
|
||
(Bereich Technik UG). Danach SSID deaktiviert; `cfg-infra`/`dp-infra`/`sec-infra` bleiben auf
|
||
User-Wunsch **inaktiv am Manager stehen**, damit der Weg beim naechsten Thread-Geraet ein
|
||
Zweizeiler ist. Beide Akten geschlossen, `crs326-core` transaktionsfrei.
|
||
⇒ **Der Workaround ist damit bewiesen** — und TP12.1 bleibt trotzdem offen: er umgeht das
|
||
fehlende IPv6-Routing, er behebt es nicht.
|
||
- ✅ **Der Weg, der das Problem dauerhaft erledigt:** Matter-Server-Option **`ble_proxy: true`** (ab HA
|
||
2026.06, hier 2026.8.3). Dann kommissioniert **HA selbst** über seinen Bluetooth-Stack; das Telefon ist
|
||
raus und die VLAN-Grenze wird nie berührt. Braucht eine **connectable** BLE-Quelle — Shelly-Proxies sind
|
||
reine Advertisement-Scanner (kein GATT), ein **ESP32 als ESPHome-BT-Proxy** (`active: true`) reicht.
|
||
Der **ESPHome Device Builder ist bereits installiert**. Damit wäre TP12.1 für Matter **optional**.
|
||
🔴 **User-Entscheid 2026-08-22: ESP32 wird NICHT angeschafft.** Damit bleibt `ble_proxy` zwar an
|
||
(schadet nicht, kostet nichts), ist aber **wirkungslos** — HA hat keine connectable BLE-Quelle und
|
||
wird auch keine bekommen. **Konsequenz, die man beim nächsten Thread-Gerät kennen muss:** der Weg ist
|
||
dann wieder die temporäre `-infra`-SSID nach dem Runbook. Die ist inzwischen ein Zweizeiler, weil
|
||
`cfg-infra`/`dp-infra`/`sec-infra` am Manager stehen bleiben — aber sie ist jedes Mal ein
|
||
Config-Change am Core plus eine Transaktion. Wer das dauerhaft loswerden will, muss **TP12.1 bauen**
|
||
(v6-Firewall zuerst); ein dritter Weg ist damit nicht mehr vorgesehen.
|
||
|
||
- 🔎 **Nebenbefund 2026-08-22, gehört zur Thread-Gesundheit, NICHT zu IPv6:** drei Matter-Knoten
|
||
flappen dauerhaft — **`@1:a` („Diele OG/Treppenauge")**, **`@1:f`** und **`@1:10`**. Muster im
|
||
Matter-Server-Log: `IpServiceStatus … Resolving (address is unreachable)`, dann
|
||
`offline grace period expired, marking unavailable`, im Abstand von 15–30 Minuten, über Stunden.
|
||
Die übrigen ~20 Knoten sind stabil, es ist also **kein** Border-Router- oder Fabric-Problem.
|
||
⚠️ **Vor einer Diagnose die Sonderlage von heute abziehen:** der OTBR ist am 2026-08-22 um 17:33
|
||
neu gestartet (Knoten fielen kurzzeitig von OMR `fd92:…` auf Mesh-Local `fd66:…` zurück), und ap1
|
||
war zwischenzeitlich vom Netz. Erst nachsehen, ob das Flappen **danach** noch besteht — sonst
|
||
diagnostiziert man das Nachbeben eines Umbaus. Erste Verdachtsrichtungen, wenn es bleibt:
|
||
Thread-Routing/Parent-Wechsel dieser drei Knoten, schwache Funkstrecke zum nächsten Router-Knoten,
|
||
oder eine ICD-/Sleepy-End-Device-Eigenheit dieser Geräteklasse.
|
||
- **Abh.:** hängt an nichts. **Blockiert nichts** — außer künftige Thread-Geräte, solange weder
|
||
`ble_proxy` noch die `-infra`-SSID steht. Reihenfolge innerhalb des TP ist zwingend:
|
||
**v6-Firewall nachbauen → dann erst Adressen vergeben.** Gehört unter TP12 (Gesamtkonzept 3 Standorte),
|
||
ist aber allein für BF ausführbar.
|
||
|
||
### TP13 — „Botschafts-WLANs" ▸ 🔜 **AUSFÜHRBAR (Runbook 2026-08-19)** — `docs/tp13-botschafts-wlan-runbook.md` · fun/low-prio
|
||
> 📌 Am 2026-08-19 überarbeitet: der Technikteil beschrieb noch Standalone-APs (SSIDs entstehen seit
|
||
> TP28 am **Manager**, also ein Deploy statt zwei), und der Entwurf „nur `ssid=`, kein Security" war ein
|
||
> **Denkfehler** — eine SSID ohne Security *ist* ein offenes WLAN, also genau der Fall, den der
|
||
> Rechtsvorbehalt ausschließen soll. Richtig: PSK, den niemand kennt. Dazu die Datapath-Falle
|
||
> (Slave-VAP ohne `vlan-id` landet ungetaggt in der Bridge — dieselbe Mechanik wie der 14-h-Ausfall).
|
||
- **Idee:** eine/mehrere **Broadcast-only-SSIDs als „schwarzes Brett"** an die Funk-Nachbarschaft — der SSID-Name *ist* die Botschaft (z.B. `Kanal-11-bitte-freihalten`, `WLAN-Tuning-siehe-Portal`). Optional ein **offenes WLAN mit Captive-Portal** auf eine **statische Info-HTML** (Kanalabsprache, Auto-Kanalwechsel-Bitte). Leitgedanke: wer die SSID sieht, kann auch stören → die Infoseite erreicht genau die Zielgruppe.
|
||
- ~~**Technik (jetzt einfach, da WLAN standalone seit TP3-Rebuild):** reine Namens-SSIDs = zusätzliche `/interface/wifi` Slave-VAPs auf wifi1/wifi2 mit eigener `configuration` (**nur `ssid=`, kein Security/Datapath**)~~ ⚠️ **Überholt 2026-08-19, in zwei Punkten:**
|
||
- **(a) WLAN ist seit TP28 nicht mehr standalone.** SSIDs entstehen am **Manager `crs326-core`** (9 `configuration`-, 6 `datapath`-Objekte, 4 `provisioning`-Zeilen — live gelesen 08-19). Das ist für TP13 sogar einfacher: **ein** Deploy statt zwei.
|
||
- **(b) 🔴 „kein Security" *ist* ein offenes WLAN** — genau das, was der Rechtsvorbehalt unten ausschließen will. Richtig ist eine SSID **mit** WPA2/WPA3-PSK, den niemand kennt: sie beacont den Namen, ist aber nicht beitretbar.
|
||
- Captive-Portal-Variante (RouterOS-**Hotspot**, isoliertes VLAN, `walled-garden`) bleibt als Option bestehen — s. Rechts-Gate unten.
|
||
- ~~**Umsetzungs-Skizze:** … Beacon-SSIDs in `routeros/.secrets/ap{1,2}-prov-base.rsc` ergänzen …~~ → diese Dateien sind für den WLAN-Teil **Historie** (die CAPs sind auf die `cap`-Zeile eingedampft). **Aktuelle Umsetzung: `docs/tp13-botschafts-wlan-runbook.md`** (2026-08-19).
|
||
- 🔴🔴 **Die Falle, die dort ausführlich steht:** ein Slave-VAP **ohne explizite `vlan-id` am Manager** landet ungetaggt in `bridge` — weil der CAP vom Manager-Datapath **nur die `vlan-id`** übernimmt. Das ist dasselbe Leck wie TP3.1 (`cfg-main-2g` ohne datapath) und dieselbe Mechanik wie der 14-h-Ausfall vom 08-18. Die Botschafts-SSID braucht deshalb einen Datapath mit **totem** `vlan-id` — PSK **und** toter Datapath, nicht eines von beidem.
|
||
- ✅ **VAP-Budget geprüft (live 08-19), unkritisch:** `max-interfaces=16` je Radio, belegt sind 4 (5 GHz) bzw. 5 (2,4 GHz). **Die Grenze ist Airtime, nicht die Zahl** — jeder VAP beacont eigenständig. Empfehlung: **genau eine SSID, auf 2,4 GHz** (mehr Reichweite = mehr Nachbarn, und 5 GHz bleibt für Nutzverkehr frei).
|
||
- ⚠️ **Recht vor Scharfschalten prüfen (User privat, AT):** offenes WLAN + Portal berührt Impressumspflicht/Störerhaftung — **reine Namens-SSIDs ohne offenen Zugang sind unkritisch** (kein Fremd-Datenverkehr); im Zweifel nur die Beacon-Variante.
|
||
- **Abh.:** ~~TP3-Rebuild ✅ (Standalone-APs — SSID-Änderung = 1 Deploy/GC-Job)~~ → **TP28 ✅** (CAPsMAN auf dem Core) + **TP22 ✅** (GC-Deploy-Pipeline). SSID-Änderung = **ein** Deploy auf den Core; Direkt-Edit am Gerät ist durch den Governance-Entscheid ausgeschlossen.
|
||
|
||
### TP14.4 — Session D: CF-Access-Profil für Claude ▸ 🔜 **AUSFÜHRBAR (Runbook 2026-08-19)** — `docs/tp14.4-cf-access-token-runbook.md` · nicht cutoff-kritisch
|
||
> 📌 Ist-Stand geprüft: in `mcp/cloudflare/.env` stehen **weder** `CF_ACCESS_CLIENT_ID` **noch** `_SECRET` —
|
||
> es ist also nichts halb erledigt. ⚠️ Das Secret existiert **nur im Moment der Rotation**.
|
||
> Abnahme braucht **beide** Fälle (mit Headern antwortet / ohne Header 403), sonst ginge ein
|
||
> bypass-Hostname wie `vw` fälschlich als Erfolg durch. Kein CF-Write erfolgt.
|
||
- 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.
|
||
|
||
### TP16 — CF-Tunnel: Round-Robin killen + reziprokes HA ▸ ✅ **HA WIEDER VOLL (2026-08-24)** — Kern 2026-07-10 — `docs/cf-tunnel-mapping.md §10 + §15`, #1033
|
||
- ✅ **GEFIXT 2026-08-24 — Ursache war ein `docker stop`, kein QUIC-Flap.** Die beiden macmini-Connectoren
|
||
(`cloudflared-bfnas`, `cloudflared-bfpve`) standen auf `Exited (0)`, **beide `finishedAt` auf derselben
|
||
Sekunde `2026-07-21T13:00:53`**, bei korrekt gesetztem `restart=unless-stopped` und `http2`. Fix =
|
||
`docker start` der zwei, sonst nichts. **Abnahme: alle drei bf-Tunnel je 2 Connectoren / 8 conns,
|
||
und 36 Anfragen über 6 FQDNs beider betroffener Tunnel mit `530er = 0`.** Details `§15`.
|
||
- 🔴 **Die Datierung im Befund unten war falsch:** degradiert **seit 2026-07-21**, nicht seit dem 08-16 —
|
||
der 08-16 war der Tag der *Entdeckung*. **Vier Wochen unbemerkt.**
|
||
- 🔴 **Die eigentliche Lehre ist die Nachweislücke:** ein Tunnel mit 1 von 2 Connectoren meldet
|
||
`healthy`. Über den Tunnel-Status ist die Degradation **unsichtbar**, nur die **Connector-Anzahl**
|
||
zeigt sie. ⇒ Check nach **TP11**: `connectoren < 2` je bf-Tunnel = Alarm. **Ein HA-Design ohne
|
||
Zähler auf die Redundanz ist kein HA-Design.**
|
||
- 📜 **Ursprünglicher Befund 2026-08-16 (Historie, Ursache s.o.):**
|
||
- `bf-mm` = **2 Connectoren** (je 4 conns) ✅ · `bf-nas` = **1 Connector** (3 conns) 🟠 · `bf-pve` = **1 Connector** (4 conns) 🟠.
|
||
- **Bedeutung:** die Dienste hinter `bf-nas` (gitea, grafana, pve-mcp, **vw**, ds-bf, nautobot) und `bf-pve` (pve, ccu, iobroker, z2m25, portainer, **hass**, ha-mcp) hängen jeweils an **einem einzigen** cloudflared-Prozess. Fällt dessen Host aus, sind sie weg — genau der Zustand, den TP16 beseitigt hat. **Kein `530`/Round-Robin-Problem** (das bleibt strukturell gelöst), sondern **Redundanzverlust**.
|
||
- ~~**Bekanntes Muster:** identisch zum Incident vom 2026-07-16~~ 🔴 **war es NICHT** — s. Fix oben. Ursprungstext:
|
||
**Bekanntes Muster:** identisch zum Incident vom 2026-07-16 (macmini-Connectoren flappten unter QUIC → Fix `TUNNEL_TRANSPORT_PROTOCOL=http2`). Diesmal sind die *macmini*-Connectoren oben (bf-mm hat 2), es fehlt je **ein** Gegenstück auf NAS/pve. **Nächster Schritt (eigene Session):** auf NAS und pve nachsehen, welcher der beiden cloudflared-Container nicht läuft, und ob es der `http2`-Konventionsfall ist.
|
||
- ✅ **GELÖSCHT 2026-08-24 (User-Entscheid „alle vier"):** `openclaw`, `coolify`, `johnny2`,
|
||
`TeilzeitCharlie` sind über die CF-API entfernt, alle vier mit `success: true`. **Gegenprobe: 5 Tunnel
|
||
verbleiben** (`bf-mm`/`bf-nas`/`bf-pve` je 2 Connectoren, `fm` und `terpene-db` je 1 — beide
|
||
standortfremd bzw. eigenes Projekt, kein Defekt). Damit sind auch die letzten **VLAN1-Origins**
|
||
(`10.40.41.147/.148/.139/.133`) aus der Cloudflare-Config verschwunden — der Rest von TP19.
|
||
- 📜 **Ursprünglicher Nebenbefund (Historie):** Alt-Tunnel mit VLAN1-Origins (alle `down`, 0 Connectoren, daher aktuell harmlos):** `openclaw` (→ `10.40.41.147/.148/.139`), `coolify` (→ `10.40.41.133`), `johnny2`, `TeilzeitCharlie`. ⚠️ **`openclaw` beansprucht `n8n.emsenhuber.net`, das produktiv auf `bf-mm` läuft** — käme der Tunnel je wieder hoch, entstünde exakt das alte Round-Robin-Muster auf diesem Hostname. **Empfehlung: die vier toten Tunnel löschen** (user-gated, Löschen ist nicht umkehrbar)
|
||
🔵 **Begründung korrigiert 2026-08-24:** die Sorge „käme der Tunnel wieder hoch, entstünde das alte
|
||
Round-Robin auf diesem Hostname" ist **so nicht richtig** — **welcher Tunnel einen Hostname bedient,
|
||
entscheidet das DNS**, und live zeigt **kein einziger CNAME** auf die vier (`n8n` zeigt korrekt auf
|
||
`bf-mm`). Ein hochkommender `openclaw` wäre inert. Der reale Pfad ist ein *aktives*
|
||
`cloudflared tunnel route dns`, das den CNAME umschreibt. ⇒ **Löschen bleibt richtig — als Hygiene
|
||
und um genau diese Falle zu entfernen, nicht als Störungsbehebung.** — gehört fachlich zu **TP19** (VLAN1-Retirement: `.41.133` war eines der „unidentifizierten VLAN1-Geräte" aus TP19 B1.2 — hiermit als coolify-Origin identifiziert).
|
||
> **✅ Umgesetzt (2026-07-10, eigene Session):** reziprokes **3-Tunnel-HA** live (bf-nas `70d6a4cf` / bf-pve `f7fdf3a6` / bf-mm `7a322261`), 6 Connectoren (2/Host, je Fremdhost), 19 FQDNs Loop-Smoke **0×530**, HA per Host-Ausfall-Sim bewiesen. Round-Robin-#1033 **strukturell weg**. As-Built = `docs/cf-tunnel-mapping.md §10`. **Schritt 5 Access-Strang: ~~`vw` bypass→geschützt via WARP~~ — 🔴 GESTRICHEN (User-Constraint 2026-08-14, s. TP16.2). Verbleibend offen: `hass`/`ha-mcp` 400/403; `gateway-dw` (WG→404) klären.**
|
||
- **Symptom (User):** FQDNs über CF werden **sporadisch / „sehr unsicher"** aufgelöst = #1033-Muster zurück („jeden 3. Schuss eine Niete"). User hat alle 3 cloudflared-Images + Container aktualisiert/neu gestartet.
|
||
- **✅ Root-Cause endgültig (User-Diagnose bestätigt): macvlan/ipvlan-Host-Isolation.** Ein cloudflared-Connector ist **blind für macvlan/ipvlan-Container auf seinem EIGENEN Host** (Linux-Kernel-Regel), erreicht aber Fremdhost-L2 + co-lokierte Bridge-Container. Mehrere Connectoren auf EINEM Tunnel = geteilter Ingress + CF-**Round-Robin** → jede Origin, die auf dem Host *eines* Connectors liegt, fällt bei ~**1/N** aus. **Strukturell**, nicht durch „Origins fixen" heilbar. Bsp: `omada .20.175`/`pve-mcp .20.173`/`gitea .20.157`/`grafana .20.109` = NAS-macvlan → vom NAS-Connector NICHT erreichbar.
|
||
- **Invariante:** innerhalb EINES Tunnels ist **kein Route→Connector-Pinning** möglich (alle Connectoren teilen Ingress, CF round-robined). „Ausgewählte Routen für ausgewählte Connectoren" = **getrennte Tunnel**.
|
||
- **Ziel-Design (HA): reziproke Per-Host-Tunnel** — jeder Host-Dienst wird von den Connectoren der ANDEREN Hosts bedient (nie vom eigenen):
|
||
|
||
| Tunnel (= Dienste eines Hosts) | Connectoren auf | HA |
|
||
|---|---|---|
|
||
| pve-Dienste | NAS + macmini | 1 abdrehbar |
|
||
| NAS-Dienste (omada/pve-mcp/gitea/grafana/…) | pve + macmini | 1 abdrehbar |
|
||
| macmini-Dienste (`.20.167`) | pve + NAS | 1 abdrehbar |
|
||
|
||
3 Tunnel, jeder Host fährt 2 cloudflared → Host-Ausfall ⇒ jeder Tunnel ≥1 Connector = **voll HA**. Voraussetzung: jede Origin L2-erreichbar.
|
||
- **Ist-Inventory (live 2026-07-10, s. `cf-tunnel-mapping.md §9`):** Ingress v119, 24 Hostnames, 2 live Connectoren. **DOWN/stale:** `pbs .20.135`, `nginx-bf .41.123` (noch VLAN1!), `hermes`+`hermes-geli .20.167`. **Zu klären (Host/Netz):** `vw .20.127`/`ccu .20.232`/`iobroker .20.107`/`z2m25 .20.106` — ggf. `vw`/`ccu` auf VLAN20-macvlan heben. `gateway-dw` (192.168.178.1) = WG-Sonderfall.
|
||
- **CF-Access-Strang (verzahnt, s. TP11 Track-B):** `hass`-**400 in der HA-iPhone-App = CF-Access davor** (Companion-App kann Access-Login nicht) → Fix **ohne Passthrough**: iPhone in **WARP** enrollen (Device-Identity erfüllt Access; „Warp Login App" existiert bereits `allow:christoph`). ~~⚠️ **`vw` = aktuell `bypass` (öffentlich!)** → von bypass auf geschützt heben.~~ 🔴 **Überholt (2026-08-14): `vw` BLEIBT bypass — harter User-Constraint, Härtung stattdessen über TP16.2.** „Passthrough-Hosts sind die kritischen" (User).
|
||
- **Umsetzungs-Reihenfolge (für die eigene Session):** (1) **Reachability-Matrix je Host** (pve/NAS/macmini → jede Origin, read-only) BEVOR gruppiert wird; (2) **Interim-Sofortfix:** bf-Tunnel auf **pve-only** (pve erreicht alles außer gateway-dw) → 0×1033, reversibel; (3) reziprokes 3-Tunnel-HA bauen (CNAMEs re-pointen, 6 Connectoren, Token je Tunnel; NAS/macmini-Alt-Token entfernen); (4) tote Origins bereinigen; (5) Access-Strang (WARP + vw); (6) Loop-Gegentest.
|
||
- Abh.: TP14.1/14.2 ✅. Verwandt: **TP11** (CF-Tunnel-Track-B/C + Access), #1033. SSoT: `docs/cf-tunnel-mapping.md`. Memory `project_cf_tunnel_1033`.
|
||
- ✅ **Incident 2026-07-16 (User unterwegs, WARP „spinnt", keine BF-IPs mehr) ▸ Public-FQDN-Teil gefixt:** macmini-Connectoren (`cloudflared-bfnas`/`-bfpve`) flappten live unter QUIC/UDP (Docker-Desktop-Bridge-NAT) → `bf-nas`/`bf-pve` von 2 auf 1 Connector degradiert (HA faktisch weg, nicht down). Fix: beide auf `TUNNEL_TRANSPORT_PROTOCOL=http2` umgestellt (rollend, 0 Downtime), wieder voll HA. Konvention: macmini-Connectoren künftig immer `http2`. **ICMP-Befund (getrennt, strukturell):** TCP über die WARP-Route ging überall, ICMP nicht (fm 36 %/bf 100 %/dw 100 % Loss) — cloudflared ist QUIC-Proxy, kein WireGuard. **Kurz als „bf-pve-Route wegen USV" notiert (Commit `1c1a676`) → REVIDIERT** (Begründung invertiert: bf-pve-Connectoren laufen auf NAS+macmini, nicht pve). Details: `docs/cf-tunnel-mapping.md §13`. **→ Richtungswechsel für Private Access = TP16.1.**
|
||
|
||
### TP16.2 — Härtung der öffentlich erreichbaren Dienste (bypass-Strang) ▸ 📋 KONZEPT (2026-08-14), Umsetzung offen
|
||
- 🔴 **HARTER CONSTRAINT (User 2026-08-14): `vw` wird NICHT WARP-/Access-geschützt.** Gilt dauerhaft, nicht „vorerst". **Begründung (technisch belastbar, nicht Bequemlichkeit):** die Bitwarden-Clients (Browser-Extension, Mobile, Desktop) können den **interaktiven Access-Login nicht durchführen** — dasselbe Muster, das die HA-Companion-App auf `400` laufen lässt. Ein Passwortmanager hinter einem Client, den man im Notfall (fremdes Gerät, Neuinstallation, Handyverlust) nicht installieren kann, ist ein **Aussperr-Risiko gegen den eigenen Schlüsselbund** — und damit gefährlicher als das, wovor die Maßnahme schützen soll. **Die frühere TP16-Schritt-5-Absicht „vw von bypass auf geschützt heben" ist damit gestrichen.**
|
||
- **Ziel stattdessen: Schutz erhöhen, ohne Clients zu brechen** — gilt für **alle** bypass-Dienste (`vw`, `hass`, `gitea`).
|
||
- ✅ **Schicht 0 — bereits vorhanden, nicht unterschätzen:** keine offenen Ports, **alles ausschließlich über CF-Tunnel** (3-Tunnel-HA, TP16), TLS terminiert an der Edge, DDoS-Schutz. Der Origin ist nicht direkt erreichbar. Das ist bereits mehr als bei den meisten Selfhostern.
|
||
- ✅ **Schicht 1 — chirurgisch: Access nur auf die Pfade, die kein Client anfasst.** CF Access wertet **die spezifischere Application zuerst** → man kann eine **zweite Access-App auf `vw.emsenhuber.net/admin*`** legen (echte Policy) und den Host im Übrigen auf bypass lassen. **Das Admin-Panel wird ausschließlich per Browser bedient — dort funktioniert der Access-Login einwandfrei**, während `/api`, `/identity` und `/notifications` (die Client-Pfade) unberührt bleiben. Kein Client bricht, das wertvollste Ziel ist weg.
|
||
- Alternativ noch härter: **Admin-Panel ganz aus** (`ADMIN_TOKEN` leer lassen) und nur bei Bedarf temporär aktivieren.
|
||
- Analog für **`gitea`**: `/admin*` + Registrierung path-scoped schützen (Git-über-HTTPS mit Token bleibt offen).
|
||
- ✅ **Schicht 2 — App-eigene Bremsen (kostenlos, sofort):** Vaultwarden hat Rate-Limits **per Default an** (`LOGIN_RATELIMIT_SECONDS=60`, `LOGIN_RATELIMIT_MAX_BURST=10`, dazu `ADMIN_RATELIMIT_*`) → Werte prüfen/verschärfen; `SIGNUPS_ALLOWED=false`, `INVITATIONS_ALLOWED=false`, `SHOW_PASSWORD_HINT=false`, **2FA erzwingen**. HA hat `ip_ban_enabled` + `login_attempts_threshold`. Gitea: Registrierung aus.
|
||
- ✅ **Schicht 3 — Edge-Regeln (CF), wirkt für alle Dienste gemeinsam:** WAF-Custom-Rules + **Rate Limiting gezielt auf die Login-Endpunkte** (`/identity/connect/token` bei vw, `/auth/login` bei HA, `/user/login` bei Gitea); optional Geo-/ASN-Filter. ⚠️ **Free-Plan-Kontingente vorher im Dashboard prüfen** (nicht belegt).
|
||
- ✅ **Schicht 4 — ein Mechanismus für alle: CrowdSec (oder fail2ban) mit Cloudflare-Bouncer.** Bans landen dann **an der Edge** statt erst am Origin. Vaultwarden hat ein offizielles Fail2Ban-Setup-Wiki (Log-Muster für Login **und** Admin-Panel).
|
||
- 🔴 **Voraussetzung für Schicht 2–4, und der eigentliche Knackpunkt: die echte Client-IP muss am Origin ankommen.** Hinter dem CF-Tunnel sieht die Anwendung sonst **nur die IP des cloudflared-Containers** → jede IP-Sperre bannt den Tunnel statt den Angreifer = **Selbstaussperrung aller Nutzer**, und Rate-Limits greifen ins Leere. **Genau dieser Defekt ist bereits offen dokumentiert:** HAs `trusted_proxies` fehlen `.20.10`/`.20.167` (TP16, `hass`-400-Strang). ➡️ **`X-Forwarded-For`/`CF-Connecting-IP` sauber durchreichen ist Schritt 1 dieses TPs**, alles andere baut darauf auf.
|
||
- **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) ▸ ⏳ **§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.
|
||
**Abnahme vollständig:** private IP mit WARP von außerhalb des LAN 302 in 0,16 s · die übrigen fünf
|
||
`bf-nas`-Hostnames unverändert · und der Punkt, den man übersieht — **Golden Config lief durch:
|
||
Backup → Intended → Compliance, 104/104, 0 Drifts.**
|
||
- 🎯 **Der Testaufbau war der einzige, der etwas beweist:** die Session lief auf einem Host im
|
||
Hotspot-Netz `172.20.10.13` mit aktivem WARP, also **nicht im BF-LAN**. Aus dem LAN heraus wäre
|
||
der Zugriff ohnehin gegangen und hätte nichts gezeigt (Merkregel `feedback_adresse_lesen_nicht_ansagen`).
|
||
- 🔴 **Befund, der die Abnahmetabelle des Runbooks korrigiert: der öffentliche FQDN taugt nicht als
|
||
Test.** `nautobot.emsenhuber.net` liefert **vor und nach** dem Schnitt HTTP 302 — der kommt von
|
||
**Cloudflare Access**, nicht vom Origin (`location: emsenhuber.cloudflareaccess.com/cdn-cgi/access/…`,
|
||
JWT `auth_status: NONE` bei `is_warp: true`). Access greift **vor** dem Tunnel. ⇒ Gültiger Nachweis
|
||
ist **das Zurücklesen der Ingress-Config**, nicht die Antwort des Frontends — dieselbe TP22/TP23-Regel.
|
||
- 📌 **Und die Erwartung wird damit ehrlicher:** Nautobot war nie ohne Access-Login öffentlich
|
||
erreichbar. Der Schnitt schließt **keine offene Tür**, er entfernt eine **überflüssige zweite**.
|
||
- ⚠️ **Vorprüfung, die vor jedem weiteren Schnitt zu wiederholen ist:** hängt Werkzeug am
|
||
öffentlichen Namen? Hier nein — `gc-configplan.py` spricht `http://10.40.20.30:8080/api`, und
|
||
kein Repo-Skript nutzt den FQDN.
|
||
- ✅ **Betriebsfenster ausgewertet 2026-08-30 (Tag 6/7): `nautobot` bleibt mesh-only.** Kein Rückweg
|
||
gebraucht, kein Ausfall. `nautobot.emsenhuber.net` hat inzwischen auch **keinen DNS-Record** mehr
|
||
(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`
|
||
und die drei CF-Access-SSH-Ziele `82unkpb5` (macmini) · `3k23uow7` (NAS) · `j44d6tua` (pve).
|
||
Ingress-Bilanz: `bf-mm` 6→4 · `bf-nas` 6→3 · `bf-pve` 9→2. Catch-all in allen dreien geprüft letzte.
|
||
- **`pbs` und die SSH-Ziele standen nicht in der Kandidatenliste** — User-Entscheid 2026-08-30: beide
|
||
mesh-only. PBS erreicht man über `10.40.20.135:8007`, SSH direkt per IP, jeweils mit WARP.
|
||
- ✅ **Zwei Namen zunächst NICHT geschnitten: `gitea-mcp` und `n8n-mcp` — am selben Tag per F1 nachgeholt.**
|
||
Die TP16.3-Vorprüfung („hängt Werkzeug am öffentlichen Namen?") schlug hier an:
|
||
`gitea-mcp.emsenhuber.net` stand in **11 Dateien** (nicht 9 — beim Edit kamen zwei
|
||
Worktree-Kopien dazu, s. F1) — `~/dev/{bf-network-r6,bf-network-r6.opencode,blaueblueten-n8n,
|
||
dachterrasse,ha,ha-xstate,poulios-recherche,valamar}/.mcp.json` plus `bf-infra/opencode.jsonc` —
|
||
und `~/.claude.json` nutzte zusätzlich `n8n-mcp.emsenhuber.net`. **TP35 Schicht 1 hatte nur die
|
||
globale `~/.config/opencode/opencode.jsonc` auf LAN-IP migriert**; Projekt-Ebene und
|
||
`~/.claude.json` waren zurückgeblieben. Regel „erst migrieren, dann schneiden" eingehalten:
|
||
**F1 hat migriert, danach fiel der Schnitt** (Ingress `bf-mm` 4→2, beide DNS-CNAMEs gelöscht).
|
||
- ✅ **Abnahme (2026-08-30), beide Richtungen gemessen:**
|
||
- **intern trägt weiter:** AdGuard löst alle neun Namen unverändert auf `10.40.20.40` (Split-Horizon,
|
||
TP31 Schritt 4), Caddy antwortet über HTTPS mit **HTTP 200** (`pve` 0,08 s · `portainer` 0,03 s ·
|
||
`ds-bf` 0,91 s · `z2m25` 0,03 s · `pbs` 0,03 s).
|
||
- **öffentlich weg:** `pve`/`portainer`/`ha-mcp`/`pbs` gegen `1.1.1.1` → **NXDOMAIN**.
|
||
- **Public-Strang unberührt:** `vw`/`hass`/`n8n`/`gitea` weiterhin **HTTP 200** über Cloudflare.
|
||
- Nach TP16.3-Regel ist der gültige Nachweis das **Zurücklesen der Ingress-Config**, nicht die
|
||
Antwort des Frontends — die Bilanz oben stammt aus dem `GET` nach dem `PUT`.
|
||
- 📦 **Rollback:** `docs/cf-backup/{bf-mm,bf-nas,bf-pve,bf-public}-ingress-20260830-vor-tp16.3-schnitt.json`
|
||
+ `docs/cf-backup/dns-emsenhuber.net-20260830-vor-tp16.3-schnitt.json` (alle `success:true`).
|
||
Ingress zurück = ein `PUT` je Tunnel; DNS zurück = CNAME auf `<tunnel-id>.cfargotunnel.com`, proxied.
|
||
|
||
#### 🔴 Blocker: der Abbau der drei Alt-Tunnel haengt an einer Vor-Ort-Session
|
||
Der Schnitt endet hier, weil `bf-public` **noch keinen Connector hat** (0 Verbindungen, angelegt
|
||
2026-08-27) und die vier Public-Dienste weiterhin auf `bf-mm`/`bf-nas`/`bf-pve` liegen. User-Entscheid
|
||
2026-08-30: der Connector gehört auf **ap1/ap2** (TP31-Begründung: der AP ist eine eigene Maschine,
|
||
kein Docker-Host — gegenüber macvlan/ipvlan sind wir dort nicht blind).
|
||
|
||
**Live gelesen am 2026-08-30 — der Preis ist nicht bezahlt:**
|
||
```
|
||
ap1: /system package print → routeros 7.24, wifi-qcom 7.24 (2 Pakete, KEIN container)
|
||
ap1: /system device-mode print → mode: home · container: no · fetch: no · scheduler: no
|
||
```
|
||
⇒ Es fehlt **beides**: das `container`-Paket (extra `.npk`, arm64) **und** `device-mode container=yes`,
|
||
und Letzteres verlangt die **physische Bestätigung am Gerät** — ap1 Technik UG, ap2 Technik OG.
|
||
`fetch: no` heißt zusätzlich: das Image kann der AP nicht selbst ziehen, es muss als `.tar` hoch und
|
||
über `/container add file=` importiert werden. **Das ist eine Vor-Ort-Session, kein Remote-Schritt.**
|
||
→ Runbook-Bedarf als Follow-up **F2**; bis dahin bleiben die drei Alt-Tunnel mit je 2 Connectoren stehen.
|
||
|
||
#### 📌 Follow-ups aus dieser Session (2026-08-30)
|
||
- ✅ **F1 — `gitea-mcp`/`n8n-mcp` FQDN→LAN-IP, DURCH 2026-08-30.** Statt der geschätzten 10 waren es
|
||
**12 Dateien**: `~/.claude.json` (beide Namen), `bf-infra/opencode.jsonc`, 8× `dev/*/.mcp.json` und
|
||
**zwei Worktree-Kopien** in `bf-network-r6{,.opencode}/.claude/worktrees/frosty-hermann-76f9cd/`,
|
||
die erst ein Sweep fand. Danach fiel der Schnitt (s. oben). Backups je Datei.
|
||
- 🔑 **Die zwei Clients haben verschiedene Konfigpfade — das ist die Stelle, an der man Dateien übersieht.**
|
||
**Claude Code** liest `<projekt>/.mcp.json`, also **eine Datei pro Projekt**. **opencode** liest
|
||
**eine globale** `~/.config/opencode/opencode.jsonc`; projekt-lokal existiert nur
|
||
`bf-infra/opencode.jsonc`. Deshalb genügte für opencode die eine globale Migration (TP35 Schicht 1),
|
||
während Claude Code acht Einzeldateien brauchte. `.mcp.json` ist **kein** opencode-Pfad.
|
||
- 🔴 **`n8n-mcp` lässt sich NICHT ohne Bearer betreiben.** `dist/http-server.js` ruft
|
||
`process.exit(1)`, wenn `AUTH_TOKEN` fehlt („required for HTTP mode"). Damit ist er der **einzige
|
||
echte Zugriffs-Bearer** im Setup — `gitea-mcp` hat gar keine Auth, alles andere (`mikromcp`,
|
||
`hostinger`, `portainer`, `github`) sind Arbeits-Credentials der MCPs, kein Zugriffsschutz.
|
||
Der Wunsch „Bearer für alle weg" ist also faktisch schon erfüllt, bis auf den einen, der es nicht kann.
|
||
- 🔧 **`gitea-mcp` auf `10.40.20.167:3000` gebunden** (war `0.0.0.0:3000`). Zustandslos, kein Volume,
|
||
per `docker run` rekonstruiert; Token über die Prozessumgebung (`-e VAR` ohne Wert), **nicht** über
|
||
argv — dieselbe Klasse wie die `sshpass -p`-Falle. ⚠️ Der Nutzen ist klein: der macmini hat **nur
|
||
`en0`**, `0.0.0.0` war dort faktisch schon `.167`. `n8n-mcp` blieb deshalb auf `*:3001`.
|
||
- 🔴🔴 **`gitea-mcp` war funktionsunfähig — mit einem veralteten Gitea-Token, seit unbekannter Zeit.**
|
||
Der Container trug `GITEA_ACCESS_TOKEN` = `a9317c68…`, den Gitea mit **401** ablehnt; der SSoT-Store
|
||
`~/.secrets/credentials.env` hielt längst den gültigen (`bc12ad19…`, **200**). Gitea selbst war die
|
||
ganze Zeit erreichbar (`/api/v1/version` → 200). Fix: Container mit dem Store-Token neu gebaut
|
||
(Token per **stdin**, nicht argv). Abnahme = echter API-Call `user__get_my_userinfo` → `login=admin`.
|
||
⚠️ **Nicht durch den Recreate verursacht:** der Token-Hash war vor und nach dem Bind-Umbau identisch.
|
||
🔑 **Die eigentliche Lehre ist die Abnahme-Methode:** ein `initialize`-Handshake beweist nur, dass
|
||
der MCP-**Prozess** lebt — er antwortet mit `serverInfo` auch dann, wenn jeder echte Aufruf am
|
||
Token scheitert. Die erste Verifikation dieser Session sah deshalb grün aus und war es nicht.
|
||
**Für MCPs mit Backend-Credential ist die gültige Abnahme ein `tools/call`, der das Backend
|
||
wirklich anfasst** — dasselbe Muster wie „frische DHCP-Lease statt Registration-Table" bei WLAN.
|
||
Direkt daraus folgt **F5**: die Container-Envs driften vom Store, weil sie ihn nie lesen.
|
||
- ⚪ **Tote Diagnose-Permissions entfernt:** `blaueblueten-n8n/.claude/settings.local.json` hatte 3×
|
||
`Bash(dig/host/curl … gitea-mcp.emsenhuber.net)` in der `allow`-Liste (10→7 Einträge). Das waren
|
||
**keine** MCP-Verbindungen — `enabledMcpjsonServers: ["gitea-mcp"]` gibt per **Name** frei und
|
||
blieb unberührt. Projektweit geprüft: nur dieses eine Projekt betroffen.
|
||
- 📌 **Nachweis-Korrektur zur Abnahme oben:** geschnittene Namen antworten öffentlich mit
|
||
**`NOERROR` + leerer Antwort**, nicht mit `NXDOMAIN` — gegengeprüft an `pve`/`portainer`/`ha-mcp`/`pbs`
|
||
aus dem großen Schnitt, die sich identisch verhalten. Gültiger Nachweis bleibt der zurückgelesene
|
||
Ingress plus die leere Antwort.
|
||
- ⚠️ Projektübergreifend → gehört ins **Announcement** (TP35 Schicht 4), eigene Session.
|
||
Dort mitnehmen: **„achte auf opencode"** sowie den neuen Pfad `~/dev/bf-infra` / Repo `admin/bf-infra`.
|
||
- **F2 — Vor-Ort-Runbook ap1/ap2:** `container`-Paket + `device-mode`, cloudflared-Image als `.tar`,
|
||
`bf-public`-Token, danach CNAME-Umhang der vier Public-Dienste und Abbau von `bf-mm`/`bf-nas`/`bf-pve`
|
||
samt der **sechs** Container (LXC400 `.126`: `cloudflared-bfmm`+`cloudflared-bfnas` · macmini `.167`:
|
||
`cloudflared-bfpve`+`cloudflared-bfnas` · NAS `.10`: `cloudflared-bfpve`+`cloudflared-bfmm`).
|
||
⚠️ `terpene-cloudflared` (LXC400) gehört zum Tunnel `terpene-db` und **bleibt** — anderes Projekt.
|
||
Ebenso unberührt: Tunnel `fm.emsenhuber.net` (Standort FM).
|
||
- **F3 — CF-Connectoren in Nautobot abbilden** (User-Wunsch 2026-08-30, „durchgehende Netzwerkdoku
|
||
Stück für Stück"). Als **TP37** vorgeschlagen, noch nicht angelegt — wartet auf dein Okay.
|
||
- 🔜 **F4 — `n8n-mcp`-Compose versöhnen** (Befund aus F1, 2026-08-30; User-Entscheid: eigener Punkt,
|
||
nicht nebenbei). Der laufende Container stimmt mit **keiner** seiner Definitionsquellen überein —
|
||
ein `docker compose up -d` wäre deshalb **destruktiv**, es würde die interne n8n-Anbindung umstellen:
|
||
|
||
| Quelle | `N8N_API_URL` | Netz | Port |
|
||
|---|---|---|---|
|
||
| `.env` (macmini) | `http://10.40.41.167:5678` ← **totes 41.x-Schema** | — | `3000` |
|
||
| `docker-compose.yml` | `https://n8n.emsenhuber.net` (hartkodiert, ignoriert die `.env`) | `web` | `${HOST_PORT:-3000}` |
|
||
| **laufender Container** | `http://n8n:5678` ← korrekt, Container-DNS | `n8n_default` | `3001` |
|
||
|
||
🔴 **Vorsicht — die naheliegende Lösung ist die falsche.** Ein `tools/call n8n_health_check` (statt
|
||
nur `initialize`) zeigt: **n8n-mcp hat einen SSRF-Schutz und lehnt private Adressen ab** —
|
||
`"SSRF protection: Private IP addresses not allowed", apiUrl: http://n8n:5678`. Die
|
||
Container-DNS-Adresse des laufenden Containers **funktioniert also gar nicht**; sie im Compose
|
||
festzuschreiben würde den Zustand zementieren, nicht heilen. Umgekehrt war
|
||
`https://n8n.emsenhuber.net` im Compose vermutlich **Absicht**, kein Drift — nur eben über den
|
||
CF-Umweg, was der TP16.3-Stossrichtung zuwiderläuft. Ein Abschalter für den Schutz ist im Image
|
||
nicht ersichtlich (nur `TRUST_PROXY`). **F4 muss diesen Zielkonflikt lösen**, nicht nur Dateien angleichen.
|
||
⚠️ **Aktueller Ausfallumfang:** die **Doku-Tools laufen** (`search_nodes` ✅ — das ist die
|
||
Hauptfunktion von `n8n-documentation-mcp`), nur die **n8n-Instanz-Management-Tools** (`n8n_*`) sind tot.
|
||
Zu tun: Netz als `external: true` auf `n8n_default`, `N8N_API_URL` **erst nach Klärung des SSRF-Punkts**,
|
||
Image auf den laufenden Digest pinnen (`sha256:2c8ff077…` = v2.57.1; heute `:latest` +
|
||
`pull_policy: always`, was bei jedem Recreate unkontrolliert hochzieht), Bind auf `10.40.20.167`.
|
||
⚠️ **`N8N_API_KEY` ist in allen drei Quellen verschieden**; der laufende und der aus
|
||
`~/.secrets/credentials.env` wurden beide gegen die n8n-API getestet (**HTTP 200**), der `.env`-Wert
|
||
ist ungetestet. Der Bearer dagegen ist überall identisch. Volume `n8n-mcp-deploy_n8n-mcp-data`
|
||
überlebt einen Recreate, `gitea-mcp` hat gar kein Compose (reiner `docker run`).
|
||
- ✅ **F7 — `pve-mcp`-URL korrigiert (2026-08-30).** Beide globalen Configs zeigten auf
|
||
`http://10.40.20.173:3000/**mcp**`, der Server antwortet aber **nur auf dem Root-Pfad**
|
||
(live: `/` → **200**, `/mcp` → **404**) — die Session meldete entsprechend `ENDPOINT_NOT_FOUND`.
|
||
Damit widersprachen `~/.claude.json` und `~/.config/opencode/opencode.jsonc` der **eigenen
|
||
CLAUDE.md**, die den Root-Pfad seit jeher korrekt nennt („Root-Pfad, NICHT /sse"). Beide auf
|
||
`http://10.40.20.173:3000/` gezogen; `bf-infra/opencode.jsonc` war bereits richtig.
|
||
Merkregel: **wenn ein MCP „endpoint not found" meldet, zuerst die Doku gegen die Config lesen** —
|
||
hier stand die Antwort seit Monaten im Repo.
|
||
- ✅ **F6 — GUI-Apps sehen `exports.zsh` nicht: gelöst 2026-08-30 per LaunchAgent.** Aufgefallen, weil
|
||
`n8n-mcp` beim Session-Start mit **401** abgewiesen wurde, obwohl URL und Bearer stimmten.
|
||
🔑 **Ursache — und sie reicht weit über n8n-mcp hinaus:** `~/.zprofile` sourct `exports.zsh` korrekt,
|
||
aber **`.zprofile` liest nur eine Login-Shell**. Claude Desktop und opencode starten **gar keine
|
||
Shell**, sie erben die launchd-Umgebung. Damit lösen **alle `{env:VAR}`-Referenzen zu LEER auf** —
|
||
live nachgewiesen: in **keinem** der `Claude Helper`-, `claude`- und opencode-MCP-Prozesse war
|
||
`N8N_MCP_BEARER` gesetzt, während dieselbe Variable in einer Login-Shell mit `len=64` vorhanden ist.
|
||
🔴 **Das erklärt rückwirkend die Klartext-Secrets in `~/.claude.json`** (4 Router-PW + Hostinger-Token):
|
||
dort steht Klartext, *weil* `{env:}` in einer GUI-App nicht funktioniert — vermutlich kein
|
||
Schlamperei-, sondern ein Notbehelf. **Die naheliegende „Hygiene"-Umstellung auf `{env:}` hätte
|
||
`mikromcp` und `hostinger` stillgelegt.** Erst jetzt ist sie gefahrlos → Voraussetzung für **TP30**.
|
||
- **Lösung:** `~/Library/LaunchAgents/net.emsenhuber.gui-env.plist` (RunAtLoad) ruft
|
||
`~/dev/.bin/gui-env-sync.sh`; das Skript liest `exports.zsh` **zur Laufzeit** und setzt die
|
||
Variablen per `launchctl setenv`. **Kein Secret-Duplikat** — die plist enthält keine Werte, die
|
||
SSoT bleibt `exports.zsh`, neue Variablen dort werden beim nächsten Login automatisch mitgenommen.
|
||
Abnahme: **22 von 22** gesetzt, `N8N_MCP_BEARER` mit dem Hash des Store-Werts.
|
||
- ⚠️ **Damit ist die Konvention „niemals `launchctl setenv`" zu präzisieren, nicht zu brechen:**
|
||
verboten war der *Handaufruf*, weil er in keiner Datei steht und keinen Reboot überlebt. plist +
|
||
Skript sind genau die Datei-Form davon. In Kauf genommen: `launchctl setenv` übergibt den Wert
|
||
als Argument (kurz in `ps` sichtbar) — es gibt keine stdin-Variante.
|
||
- 📌 **Nach jeder Änderung an `exports.zsh` gilt:** Skript erneut laufen lassen **und die App neu
|
||
starten** — ein laufender Prozess erbt die Umgebung nicht nachträglich.
|
||
- 🔜 **F5 — Secret-Verteilung auf Container-Hosts klären** (offen aus F1). `~/.secrets/credentials.env`
|
||
ist SSoT, liegt aber **nur auf dem MacBook**; auf dem macmini existiert **kein `~/.secrets`**, und
|
||
dort laufen die Container. Ein Symlink ist deshalb physisch unmöglich. Zu entscheiden: schlanke
|
||
Ableitung mit nur den nötigen Keys je Host, oder Vollspiegelung. Zusammen mit **TP30** lösen — dort
|
||
liegt der verwandte Posten: `~/.claude.json` hält **5 Secrets im Klartext** (4 Router-PW,
|
||
Hostinger-Token), wo die opencode-Configs an denselben Stellen `{env:…}` nutzen.
|
||
🔴 **Falle beim Auslesen des Stores:** die Zeilen tragen **Inline-Kommentare**
|
||
(`CF_MM_TUNNEL_ID=7a32… # bf-mm …`). Naives `cut -d=` liefert einen 80-Zeichen-Wert, die CF-API
|
||
antwortet dann `7003 Could not route`. Ein globales `set -a; source` setzt die Variablen gar nicht
|
||
(`len=0`, Grund ungeklärt). Richtig ist gezieltes Lesen **mit** `sed -E 's/[[:space:]]+#.*$//'`.
|
||
- 📌 **Ein Argument des TP ist entfallen:** die HA-Degradierung, die §7 als Nebeneffekt anführte, ist am
|
||
**selben Tag repariert** (TP16 ✅). Der Fall steht damit allein auf dem eigentlichen Argument —
|
||
**weniger bewegliche Teile für Dienste, die öffentlich nicht gebraucht werden.**
|
||
> 📌 Der Schnitt (`nautobot` aus dem `bf-nas`-Ingress) ist ausformuliert inkl. Vorbedingungen, PUT-Ablauf
|
||
> und Rollback. Zwei Punkte stehen dort explizit, weil sie den TP sonst kippen: **Mesh-Hostname-Routes
|
||
> sind Egress und veröffentlichen nichts** (wer das verwechselt, baut den Public-Strang ab), und
|
||
> **Nautobot ist nicht nur Web-UI, sondern die Deploy-Pipeline** → zur Abnahme gehört ein Job-Lauf,
|
||
> nicht nur ein Browser-Aufruf.
|
||
> **Christophs Frage (2026-08-17):** „recherchiere, ob unser neuer wg-tunnel/cf mesh als datenpfad für die
|
||
> server/services genutzt werden kann. die containerpflicht als offizieller weg ist für mich unlogisch."
|
||
|
||
- ✅ **Die Container-„Pflicht" gibt es für den privaten Zugang tatsächlich nicht — und wir nutzen sie schon nicht mehr.**
|
||
CF-Docs (Mesh↔Tunnel-Vergleichstabelle) sind eindeutig: **Mesh** = bidirektional, L3/L4, **TCP+UDP+ICMP**, jeder
|
||
Teilnehmer bekommt eine Mesh-IP, Connector = `warp-cli` **oder ein nativer WireGuard-Peer** (unser Fall).
|
||
**Tunnel** = *inbound zum Origin*, Veröffentlichen von Hostnames, Connector = `cloudflared`, Protokolle
|
||
HTTP/S·TCP·SSH·RDP·SMB **über WebSocket geproxied**. Für „Server hinter dem Node erreichbar machen, die keinen
|
||
Client laufen lassen können" nennen die Docs ausdrücklich **CIDR-Routes auf dem Mesh-Node** als den Weg —
|
||
wörtlich für „servers, databases, printers, IoT devices".
|
||
- 🎯 **Das ist bei uns seit TP16.1 bereits scharf:** die vnet-Route `10.40.0.0/16` zeigt auf `bf-core-mesh`
|
||
(§4.4, Weg A). **Der gesamte private Datenpfad läuft schon über natives WireGuard auf dem Core, ohne jeden
|
||
Container.** Und er ist im TP5-Log-Lauf **belegt**, nicht vermutet: 137 Zeilen `in:cf-mesh`, Quelle
|
||
`100.96.0.1` → Nautobot `.20.30:8080` (62×), NAS `.20.10:6690` (46×), macmini `:22`, NAS `:5000/:5001/:443/:22`,
|
||
pve `.20.100:8006`, pve-mcp `.20.173:3000`, branch/ap1/ap2 `:22`. **Christophs Instinkt war richtig.**
|
||
- 🔴 **Wo die Container-Pflicht echt ist — und warum sie nicht willkürlich ist:** für einen **öffentlichen
|
||
Hostname**, den jemand **ohne** WARP im Browser aufruft, braucht es einen Reverse-Proxy an der Edge. Das ist
|
||
`cloudflared`. **Achtung Verwechslungsfalle:** Mesh kennt seit 2026-07-02 ebenfalls **Hostname-Routes**, und
|
||
die können auch *public* hostnames — aber das ist **Egress**: „route that hostname's traffic through the node
|
||
and egress via the node's public IP". Es **veröffentlicht nichts**. Wer das verwechselt, baut den Public-Strang
|
||
ab und ist überrascht.
|
||
- ✅ **Der reale Gewinn ist trotzdem groß, weil die meisten Dienste keinen öffentlichen Hostname brauchen.**
|
||
Kriterium: **hat der Dienst einen Client, der keinen interaktiven Access-Login kann?** (dieselbe Logik wie der
|
||
TP16.2-Constraint bei `vw`)
|
||
| Muss öffentlich bleiben (cloudflared) | Grund | Kann auf Mesh-only |
|
||
|---|---|---|
|
||
| `vw` | Bitwarden-Clients, harter TP16.2-Constraint | `pve`, `nautobot`, `portainer`, `grafana` |
|
||
| `hass` | Companion-App (400-Muster) | `pve-mcp`, `ha-mcp`, `gitea-mcp`, `ds-bf` |
|
||
| `n8n` | eingehende Webhooks von Dritten | `ccu`, `iobroker`, `z2m25` |
|
||
| `gitea` | Git-Clients / CI von außen — **zu prüfen** | |
|
||
→ Für die rechte Spalte ist der Mesh **funktional gleichwertig und operativ einfacher** (keine Ingress-Zeile,
|
||
kein Connector-Paar, keine HA-Frage). Sie erreicht man ohnehin schon per privater IP.
|
||
- 📌 **Zwei Nebeneffekte, die den Fall stärken:** (1) die **TP16-HA-Degradierung** (`bf-nas`/`bf-pve` je nur ein
|
||
Connector) verliert an Gewicht, sobald nur noch die vier Public-Dienste an cloudflared hängen — weniger Dienste
|
||
pro SPOF; (2) **ICMP + beliebiges UDP** gehen über den Mesh und über cloudflared grundsätzlich nicht (der
|
||
QUIC-Proxy war genau die Lücke aus TP16.1).
|
||
- ⚠️ **Grenzen, ehrlich benannt:** (a) der Mesh-Weg setzt **WARP auf dem Client** voraus — für Christophs eigene
|
||
Geräte gegeben, für Gäste/Fremdgeräte nicht; (b) **IPv6-CIDR-Routes brauchen MASQUE** und funktionieren mit
|
||
einem WireGuard-Device-Profil **nicht** — deckt sich mit dem v4-only-Entscheid aus TP16.1, wäre aber ein
|
||
Blocker, falls TP12 später v6 über den Mesh wollte; (c) es bleibt eine **Cloudflare-Abhängigkeit** — der Mesh
|
||
ersetzt cloudflared, nicht Cloudflare.
|
||
- **Vorgeschlagener Umsetzungsschnitt (klein anfangen, reversibel):** einen Dienst der rechten Spalte als Probe
|
||
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.
|
||
|
||
#### 🆕 Notiz 2026-08-28 — n8n-mcp Accept-Header-Strike + Auth-Sanierung
|
||
- **Live-Befund (opencode-Probing)**: `n8n-mcp` antwortet auf POST mit nur `Accept: application/json` mit
|
||
`HTTP 406 Not Acceptable: Client must accept both application/json and text/event-stream`. Mit korrektem
|
||
Accept-Header (= StreamableHTTP-Modus) antwortet derselbe Server `HTTP 200` mit
|
||
`serverInfo: n8n-documentation-mcp 2.57.1`. ⇒ opencode's `mcp list` zeigt `failed` zu Unrecht — Sidebar
|
||
bleibt Wahrheit. Volle Diagnose im Session-Anker
|
||
[`docs/session-20260828-decisions.md`](session-20260828-decisions.md), Abschnitt
|
||
„MCP-Status-Check (Cold-Start-Race + Accept-Header-Strike)".
|
||
- **Konsequenz für TP16.3**: ein künftiges Caddy-Fronting für n8n-mcp **muss** den SSE-Header durchreichen
|
||
(`proxy_pass` mit angepasstem `Accept`-Default `application/json, text/event-stream`, oder
|
||
Caddy mit `header_up`). Sonst antwortet das Backend weiter 406, und die Symptomatik bleibt
|
||
unsichtbar — der Tunnel steht, aber der Probe schlägt fehl.
|
||
- **Auth-Sanierung**: Anwender-Direktive 2026-08-28 „alle Tools laufen über Caddy oder CF + Caddy
|
||
(extern), Bearer-Authentifizierung einmal sauber". Gehört thematisch in TP16.3, **nicht** als neuer
|
||
TP — siehe Quick-Index Zeile TP16.3. Bezug zu **TP35** Schicht 2: Reihenfolge ist TP16.3 → TP35.
|
||
TP35 wartet mit Schicht 2, bis das Fronting steht.
|
||
|
||
### TP17.1 — Shelly Gen4 raus aus dem WLAN ▸ ❌ **VERWORFEN (2026-08-23) — Weg bewiesen, Ziel damit nicht erreichbar** — 📋 `docs/tp17.1-shelly-gen4-funkmigration-runbook.md`
|
||
> ❌ **User-Entscheid 2026-08-23 nach dem Pilot: stoppen und zurückbauen.** Auslöser war Christophs
|
||
> strukturelle Frage: *„zigbee sollte die antwort auf die vielen radios auf -iot geben. nutzt aber
|
||
> wenig wenn man das wifi nicht abdrehen kann bzw. es nicht mehr aktivieren kann ohne reset."*
|
||
>
|
||
> **Der Pilot hat den Weg technisch vollständig bewiesen** — `s9` lief auf `PowerStripZB`, trat ZHA
|
||
> bei, ein Zigbee-Befehl schaltete das physische Relais, kein Broadcast-Sturm. **Und er hat gezeigt,
|
||
> dass das Ziel so nicht erreichbar ist:**
|
||
> - Das Gerät **bleibt WLAN-Client** — Zigbee kommt *zusätzlich*, nicht *statt*.
|
||
> - Der Funkgewinn ist **gemessen klein**: die 12 Gen4 sind 12 von 26 IoT-Clients, bewegen aber je nur
|
||
> ~150–200 KB rx / 0,6–3 MB tx in sechs Stunden. Leerlaufende Stationen.
|
||
> - Der Preis wäre **dauerhaft**: die Zigbee-Firmware hat **keinen AP** (`WiFi.GetConfig` kennt nur
|
||
> `sta`/`sta1`/`roam`), Shelly macht **kein Zigbee-OTA**, und `Shelly.InstallAlt` braucht IP.
|
||
> WLAN aus hieße: nie wieder Firmware-Update, kein Rückweg — bei 10 Unterputz-Modulen.
|
||
>
|
||
> ✅ **Rückbau durchgeführt 2026-08-23:** ZHA-Knoten sauber entfernt (Leave **vor** dem Flash, damit
|
||
> kein Geisterrouter im 52-Geräte-Mesh bleibt), `s9` zurück auf `PowerStrip`, Config aus dem Backup
|
||
> wiederhergestellt. Compliance des Zigbee-Netzes unauffällig: alle vier roten EZSP-Zähler blieben
|
||
> **0**, Retry-Quote fiel im Delta auf 8,9 %.
|
||
> ⚠️ **Beim Rückflash zog die Firmware einen OFFENEN AP hoch** (`ap.enable=true, is_open=true`) —
|
||
> abgeschaltet und mit dem IoT-PSK abgesichert. **Nach jedem Shelly-Firmwarewechsel den AP prüfen.**
|
||
>
|
||
> 📌 **Das Runbook bleibt** — es enthält den kartierten Mechanismus (drei undokumentierte RPCs,
|
||
> Zwei-Slot-Modell), das gemessene 2,4-GHz-Bild und die ZHA-Sturmzähler mit Baseline.
|
||
> *Der Weg ist nicht gescheitert, das Ziel war mit ihm nicht erreichbar.*
|
||
> 🔭 **Einziger Restposten:** der **Frühindikator** — `Shelly.CheckForUpdate` auf einem Gen4 auf einen
|
||
> `…MT`/Thread-`alt`-Eintrag beobachten. Erscheint er, ändert sich die Rechnung.
|
||
|
||
<details><summary>Ursprünglicher Planungstext (historisch)</summary>
|
||
|
||
> **User-Formulierung (2026-08-23):** „wir lassen das pwd vorerst wie es ist. wir werden alle shelly
|
||
> gen4 auf thread umstellen — neue firmware. das machen wir im ha-projekt. das sehe ich als subpunkt
|
||
> zu pwd ändern. der vorteil: wir müssen dann weit weniger geräte umstellen, steigern
|
||
> thread-stabilität und bringen zudem mehr ruhe ins wifi."
|
||
>
|
||
> 📋 **Runbook: [`tp17.1-shelly-gen4-funkmigration-runbook.md`](tp17.1-shelly-gen4-funkmigration-runbook.md)**
|
||
> — Bestand, die drei Wege im Vergleich, Ablauf, Abnahme, Verzahnung mit der PSK-Rotation.
|
||
> 📌 **Wohnort ist das Netz-Projekt (User-Entscheid 2026-08-23).** Eine erste Fassung lag kurz im
|
||
> HA-Projekt (`ha-xstate`); sie ist dort **entfernt**, damit es keine zweite Wahrheit gibt. **HA ist
|
||
> Betroffener, nicht Eigentümer:** die Entities brechen, aber die Entscheidung ist eine Funk- und
|
||
> Netzentscheidung. *(Der Titel sagt jetzt „Thread **oder Zigbee**" — der ursprüngliche
|
||
> Thread-Zuschnitt hat den heute tatsächlich verfügbaren Weg ausgeschlossen, s. Nachtrag unten.)*
|
||
|
||
- 🔀 **Die Reihenfolge ist der eigentliche Entscheid:** die **PSK-Rotation** (Follow-up unten) wird
|
||
**nachgelagert**, nicht vorgezogen. Begründung des Users: jedes Gerät, das vorher auf Thread
|
||
wandert, muss beim PSK-Wechsel nicht mehr angefasst werden. Damit ist die Rotation **Nachfolger**
|
||
von TP17.1, nicht mehr eigenständig. *(Sie war nie dringend — Anlass war ein Passwort in einem
|
||
Sitzungstranskript, kein Vorfall.)*
|
||
- 📊 **Bestand live gesweept 2026-08-23** (`mcp/shelly/discover.py`, read-only, VLAN30): **22 von 26
|
||
erreichbar**, davon **12 Gen4** (9× 2PM Gen4 `S4SW-002P16EU`, 1× 1PM Gen4, 2× Plug S Gen4), 8 Gen3,
|
||
2 Gen2 — alle auf Firmware 2.0.0. Nicht erreichbar: s1/s2/s3 (weihnachten_*, saisonal) und **s4
|
||
`kueche_mikrowelle`** (Metallgehäuse, RSSI −93 — der Härtefall des WLAN-Betriebs).
|
||
- 🔴 **Die Prämisse „neue Firmware" trägt so nicht, und das ändert die Rechnung:** **Shelly
|
||
unterstützt Matter over Thread offiziell nicht** und plant es nicht. Gen4 kann offiziell nur
|
||
**Matter over WiFi** — was hier nutzlos wäre, weil das Gerät im WLAN bliebe und den PSK weiter
|
||
bräuchte. Thread geht nur über **Dritt-Firmware** (die ESP32-C6-Hardware kann es). Das brauchbare
|
||
Projekt (`hencou/shelly_gen4_matter_module`) deckt 1/1 Mini/1PM/2PM Gen4 ab = **10 der 12** Geräte;
|
||
die zwei **Plug S Gen4 fallen raus**. Preis: Garantie und Shelly-Cloud weg, Rückweg nur mit
|
||
**UART-Vollbackup je Gerät**, PM-Kalibrierung zunächst Platzhalter.
|
||
⇒ **Selbst bei vollem Erfolg blieben 12 Shellys im WLAN** (8 Gen3 + 2 Gen2 + 2 Plug S + die 4
|
||
ausgesteckten). Die Rotation wird **kleiner, nicht überflüssig** — und Gen3 kann nie folgen, dort
|
||
fehlt 802.15.4. **Das ist Christophs Abwägung vor dem ersten Flash, nicht die des Umsetzenden.**
|
||
- 📊 **Und die Rechnung ist noch etwas ungünstiger, als „Shellys" nahelegt** (live 2026-08-23):
|
||
auf `dirtyvader-iot` hängen **26 der 27 WLAN-Clients des ganzen Netzes** — darunter Drucker
|
||
(P750W), Airfryer, SwitchBot, Twinkly, DTUBI und ein SONOFF-NSPanelPro, die von Thread **nichts**
|
||
haben. Nach zehn migrierten Gen4 blieben also grob **16 Geräte** auf dem IoT-SSID, nicht 12.
|
||
Der WLAN-Ruhe-Effekt ist real (−38 % Clients), der PSK-Effekt ist es **halb**.
|
||
- ✅ **Was netzseitig schon steht und nicht nochmal gebaut werden muss:** Thread-Border-Router läuft
|
||
mit ~30 produktiven Matter-Knoten; der **Commissioning-Weg ist erprobt** — temporäre SSID
|
||
`dirtyvader-infra` (VLAN20) nach [`matter-pairing-infra-ssid-runbook.md`](matter-pairing-infra-ssid-runbook.md),
|
||
am 2026-08-22 mit dem TIMMERFLOTTE bewiesen. `cfg-infra`/`dp-infra`/`sec-infra` stehen dauerhaft am
|
||
Manager, das Anlernen ist ein Zweizeiler plus eine Config-Transaktion.
|
||
- 🔴 **Der Umweg bleibt nötig, weil TP12.1 offen ist** (kein IPv6-Routing VLAN10↔VLAN20 → Matter-
|
||
Commissioning-Etappe 2 bricht ab), und `ble_proxy` ist wirkungslos, seit der ESP32-BT-Proxy
|
||
ausgeschlossen wurde. **Je mehr Thread-Geräte kommen, desto öfter zahlt man diesen Umweg** — das
|
||
ist das stärkste neue Argument für TP12.1, das bisher nur an einem einzigen Fühler hing.
|
||
- **Abnahme (aus TP28 gelernt):** pro Gerät ein **frischer Thread-Knoten und eine funktionierende
|
||
HA-Entity** — nicht die Anzeige in einer Tabelle. Ein Gerät, das in Thread hängt und in HA halbtot
|
||
ist, ist ein Rückschritt.
|
||
- 🔬 **Nachtrag 2026-08-23 (abends) — die Geräte selbst befragt (`Shelly.CheckForUpdate`, read-only).
|
||
Ergebnis: es gibt heute KEIN Thread-Update, aber die offizielle Alt-Firmware-Schiene existiert — sie
|
||
liefert bisher ZIGBEE.**
|
||
| Was | Stand am Gerät |
|
||
|---|---|
|
||
| Stable | **2.0.0 auf allen 22** — für **keines** ein stabiles Update verfügbar |
|
||
| Beta | `2.0.1-beta1` (Build **2026-08-19**) für alle, auch Gen3 |
|
||
| **Alt-Varianten (nur Gen4!)** | `S2PMG4ZB` „Shelly 2 PM Gen4 **with Zigbee**" · `S1PMG4ZB` (1 PM Gen4) · `PowerStripZB` (Power Strip Gen4) — je stable 2.0.0 + beta |
|
||
| Gen3 (`S3PL-*`) | **kein `alt`-Eintrag** → Alternativfunk ist eine reine Gen4-Sache, wie erwartet |
|
||
- 🎯 **Das ist die wichtigste Erkenntnis für die Planung:** Shelly liefert alternative
|
||
**Funk**-Firmware für Gen4 **über den normalen Update-Kanal**, nicht über einen Bastelpfad. Käme
|
||
eine Thread/Matter-Variante, erschiene sie mit hoher Wahrscheinlichkeit **genau dort** als weiterer
|
||
`alt`-Eintrag. ⇒ **Billiger Frühindikator statt Warten auf Pressemeldungen:**
|
||
`Shelly.CheckForUpdate` auf einem Gen4 pollen und auf einen `…MT`/Thread-Eintrag warten.
|
||
- 📰 **Presselage (recherchiert 08-23):** Shellys offizielle Linie ist **„wird evaluiert"**, nicht
|
||
„ist angekündigt" — Stand Juli 2026, mit **Flash-Platz** als genanntem Engpass. Ein Release-Datum
|
||
gibt es nicht. *(Die Thread-Firmware, die im Mai 2026 als „für Gen4 verfügbar" durch die Presse
|
||
ging, ist die **Dritt**-Firmware von automatous-io, nicht Shelly.)*
|
||
- 💡 **Und daraus folgt eine Option, die vorher nicht auf dem Tisch lag: ZIGBEE statt Thread.** Sie
|
||
erfüllt **alle drei** Ziele von TP17.1 — Geräte raus aus dem WLAN (PSK-Rotation wird billiger),
|
||
Mesh-Stabilität, Funkruhe im 2,4-GHz-WLAN — und zwar **heute, mit Hersteller-Firmware über den
|
||
offiziellen Kanal**, also ohne Garantieverlust, ohne UART-Backup, ohne Test-VID. Die Infrastruktur
|
||
steht bereits: **Zigbee2MQTT läuft als LXC 112 (`10.40.20.106`, `z2m25`)**.
|
||
⚠️ **Zu prüfen, bevor das zur Empfehlung wird:** **(a)** ob die Alt-Firmware genauso zurückrollbar
|
||
ist wie ein normales Update (Wechsel des Funkstacks, nicht nur der Version) — **offen**;
|
||
**(b)** ~~der Entity-Bruch~~ ✅ **entschärft (User 2026-08-23): der Umbau behält die
|
||
Entity-Namen** — damit fällt der teuerste Posten der Abwägung weg, für Zigbee **und** Thread
|
||
gleichermaßen *(zwei Fallen dazu im Runbook: die alte `entity_id` ist erst nach dem Entfernen
|
||
frei, und `unique_id`/Statistiken sind trotzdem neu)*;
|
||
**(c)** Kanalplanung ✅ **erhoben 2026-08-23** — es gibt **drei** 802.15.4-Netze, nicht eines:
|
||
**Thread 15** (~30 Matter-Knoten), **ZHA 20** (**52 Geräte**, SONOFF Dongle-M über
|
||
`socket://10.40.30.137:6638`), **Z2M 25** (Sonoff Dongle Plus). Gegen WLAN **ap1 ch1 / ap2 ch11**
|
||
ist das sauber — **außer ZHA ch20 (2450), das direkt auf der Unterkante von WLAN ch11 (2451)
|
||
sitzt**, ausgerechnet 52 Geräte gegen den AP mit 26 Clients. Die Lücke 2423–2451 (ZB 16/17/18) ist
|
||
unbenutzt; ⚠️ ein ZHA-Kanalwechsel müsste von 52 Geräten mitgemacht werden, schlafende und ältere
|
||
Router folgen nicht zuverlässig → **erst Energy-Scan, dann entscheiden.**
|
||
🔴 **Und der Vorbehalt, der schwerer wiegt als alle drei: Christophs Z2M-Vorfall von 2024**
|
||
(„Lichterdisko", Installation unsteuerbar, Neuaufbau). Fehlerklasse ist benennbar
|
||
(Broadcast-/Routing-Sturm — trifft ZHA genauso), Root Cause nicht mehr rekonstruierbar.
|
||
⇒ **Mitigation ohne Ursachenkenntnis = Blast Radius trennen:** die zehn neuen Router komplett in
|
||
**ein** Zigbee-Netz, und zwar in das, das **nicht** Licht und Fernbedienungen trägt. Details und
|
||
Abbruchkriterium im Runbook.
|
||
- 🩺 **HA-Gegencheck 2026-08-23: genau EIN Shelly ist offline** — `s4 kueche_mikrowelle` (23 Entities
|
||
`unavailable`). Deckungsgleich mit dem Netz-Sweep, und es ist dasselbe Gerät, das bei **RSSI −93**
|
||
hinter Metall hängt. **s1/s2/s3 (`weihnachten_*`) sind in HA nur DEAKTIVIERT**, nicht gelöscht
|
||
(Christoph 2026-08-23) — deaktivierte Entities haben keinen Zustand und tauchen deshalb in keiner
|
||
Zustandssuche auf. Die Entities gibt es also zu Recht; sie kommen mit den Geräten zurück.
|
||
*(Meine erste Formulierung „existieren gar nicht" war falsch.)* ⇒ Für die
|
||
PSK-Rotation heißt das: **drei Geräte, die niemand vermisst, aber die beim nächsten Einstecken mit
|
||
dem alten PSK kommen.** Genau dafür ist die Übergangs-`access-list` da.
|
||
- 🖨️ **Es gibt ZWEI Drucker — geklärt 2026-08-23, beide Angaben waren richtig:**
|
||
**(1) P750W = Etikettendrucker, VLAN30** (`10.40.30.5`, Reservierung auf `dhcp-v30`, ARP `vlan30`;
|
||
am 08-23 aus, `last-seen 3d15h` — deshalb nicht unter den 26 assoziierten Clients).
|
||
**(2) Brother MFC = der eigentliche Drucker, VLAN10** — live bestätigt `10.40.10.6`, MAC
|
||
`30:05:5C:78:86:20` (OUI Brother Industries), ARP `delay` = aktiv, **statisch konfiguriert, keine
|
||
DHCP-Reservierung**.
|
||
📌 **Folge für TP5:** die Merkregel „Drucker fällt mit **udp/161 (SNMP)** aus, nicht mit 631"
|
||
(`ref_printer_snmp_before_ipp`) gilt dem **VLAN-übergreifenden** Fall. Der MFC steht im **selben
|
||
VLAN** wie die Macs — dort greift die `forward`-Kette gar nicht.
|
||
- 🔀 **Reihenfolge revidiert (User 2026-08-23): PSK-Rotation und Thread-Umstellung laufen PARALLEL.**
|
||
Sie blockieren einander nicht — der Passphrase-Override je MAC (`/interface wifi access-list`,
|
||
s. Follow-up) macht den Flag-Day überflüssig: neuer PSK aufs Profil, die noch nicht migrierten
|
||
Geräte übergangsweise per MAC mit dem alten weiter. **Einzige Regel, damit die Parallelität nichts
|
||
kostet:** ein Gerät, das ohnehin auf Thread/Zigbee wandert, bekommt den neuen PSK **gar nicht** —
|
||
es kommt auf die Override-Liste und verschwindet später ganz. *(Die frühere Formulierung „blockt
|
||
die Rotation" war zu streng; die Reihenfolge war nie technisch nötig, nur ökonomisch.)*
|
||
- **Abh.:** keine harten fürs Netz. **Läuft parallel zur PSK-Rotation** (User-Entscheid 08-23,
|
||
vorher als Blocker geführt). Verwandt: **TP12.1**
|
||
(IPv6/Matter — profitiert direkt), **TP25** (jedes Gerät weniger im 2,4-GHz-Band ist Funkruhe),
|
||
**TP17** im [Archiv](teilprojekte-archiv.md#tp17) (Migrationshistorie + `mcp/shelly/`-Werkzeug).
|
||
|
||
</details>
|
||
|
||
### TP17.3 — Shelly-Regression nach der VLAN30-Migration ▸ ✅ **ERLEDIGT (2026-08-23, Folge-Session): beide Geräte repariert, Ursache `eco_mode` bewiesen** — offen nur noch der Flotten-Entscheid (gehört TP17.1) — aus TP17 herausgelöst, User-Meldung
|
||
> **User-Meldung 2026-08-23:** „vor einiger zeit hast du an den shellys änderungen vorgenommen. das
|
||
> hatte bei einigen einen bösen seiteneffekt: **lüfter-shelly musste ich doppelt drücken**. der übelste
|
||
> ist jedoch der von **diele ug: seit deinen änderungen spinnt er herum und blinkt wie ein wilder.
|
||
> sobald AP on ist, ist die symptomatik weg.** ich muss vermutlich eh alle shellys neu machen."
|
||
|
||
- 🔴 **Erste Konsequenz, sofort: der aktivierte AP auf `s23` ist ein WORKAROUND, kein Rest.** Ich hatte
|
||
ihn beim WLAN-Scan am selben Tag als „übrig gebliebenen Anker aus TP17, gehört abgeschaltet"
|
||
eingeordnet — **das war falsch**, und es hätte Christophs Notbehelf zerstört. Er ist bewusst an.
|
||
**Nicht abschalten, bevor die Ursache behoben ist.** *(Merkregel: ein Zustand, der nach einem
|
||
Verfahren „übrig" aussieht, kann eine Reparatur sein. Vor dem Aufräumen fragen, wozu er da ist.)*
|
||
✅ **Aufgelöst 2026-08-23:** die Ursache ist behoben, der AP auf `s23` ist **abgeschaltet** und wird
|
||
nicht mehr gebraucht. Der Workaround hat exakt das getan, was die Hypothese vorhersagte.
|
||
- **Symptome und Geräte:**
|
||
| Gerät | Modell | Symptom |
|
||
|---|---|---|
|
||
| **s23** `dieleug-wohnzimmer_licht` `.203` | **SPDM-002PE01EU**, app `ProDimmerx` = **Phasenanschnitt-Dimmer** (Diele UG/Licht + Wohnzimmer/Seitenlicht) | „blinkt wie ein wilder", **AP an ⇒ weg** |
|
||
| **s26** `bad_luefter` `.233` | S4SW-002P16EU (Gen4), `switch:0 in_mode=momentary` | muss **doppelt gedrückt** werden |
|
||
|
||
#### Was die Migration am Gerät wirklich verändert hat (TP17, 2026-07-13)
|
||
Vier Dinge, nicht nur die WLAN-Config: **(1)** STA-SSID/PSK · **(2)** **statische IP** statt DHCP
|
||
(`ipv4mode=static`, gw/dns `10.40.30.1` — an allen 22 bestätigt) · **(3)** **`eco_mode`** nach einer
|
||
RSSI-Politik · **(4)** AP-Anker an/aus.
|
||
|
||
#### 🎯 ✅ BESTÄTIGTE URSACHE: **`eco_mode`**, nicht die WLAN-Config
|
||
- `eco_mode` **senkt den CPU-Takt und die WLAN-Sendeleistung** — so steht es in TP17 selbst.
|
||
- Ein **Phasenanschnitt-Dimmer** braucht präzises **Nulldurchgangs-Timing**. Ein gedrosselter Takt
|
||
erzeugt Jitter in genau diesem Timing ⇒ **Flackern**. Das ist s23.
|
||
- **Warum „AP an" heilt:** ein aktiver Access Point muss beaconen, das Funkteil darf nicht in die
|
||
Sparzustände. Der AP hebt also faktisch den Energiesparbetrieb auf — **er behandelt das Symptom
|
||
genau dort, wo die Hypothese die Ursache vermutet.** Diese Korrelation ist das stärkste Indiz.
|
||
- **s26:** `momentary`-Eingang + gedrosselter Takt ⇒ die erste Flanke wird verschluckt ⇒ „doppelt drücken".
|
||
- ✅ **Gegenprobe im Bestand:** **`s12 dieleug_steckdosen`** hängt am selben Ort, ist dasselbe
|
||
Gen4-Modell wie s26 — und hat **`eco_mode=false`**. Es ist unauffällig. *(Kein Beweis, aber die
|
||
Hypothese überlebt ihn.)*
|
||
- ✅ **Die Alternativerklärung ist damit erledigt:** der Verdacht auf gerätespezifische Dimmer-Themen
|
||
(Leading/Trailing Edge, Kalibrierung, Mindestlast/LED-Treiber) hat sich nicht bestätigt — `eco_mode`
|
||
allein genügte. *Nebenbei: die Config dieses Geräts kennt für Phasenan-/-abschnitt ohnehin **keinen**
|
||
Parameter; Stellschrauben wären nur `warmup`, `range_map`, `gamma`, `min_brightness_on_toggle`
|
||
gewesen.* Der Link war mit −54 dBm die ganze Zeit sehr gut, ein reines Funkproblem war nie plausibel.
|
||
|
||
#### Der diskriminierende Test (klein, reversibel, ein Gerät) — ▶️ AUSGEFÜHRT 2026-08-23
|
||
1. ✅ `s23`: `Sys.SetConfig {"config":{"device":{"eco_mode":false}}}` — **AP zunächst AN gelassen**.
|
||
`cfg_rev` 91 → 92, `eco_mode=false` gelesen.
|
||
2. ✅ Christoph beobachtet: **„licht in diele ug ist jetzt flackerfrei"**.
|
||
⚠️ **Das ist noch kein Nachweis** — mit AP an war es vorher schon flackerfrei. Beide Hypothesen
|
||
sagen an dieser Stelle dasselbe voraus. *(Merkregel [[feedback_abnahme_wirkung_nicht_anzeige]].)*
|
||
3. ✅ **AP aus** (`WiFi.SetConfig ap.enable=false`) — damit war der Test scharf: `eco_mode=false`,
|
||
`ap.enable=false`, STA `got ip 10.40.30.203` bei −54 dBm.
|
||
4. 🎯 **ERGEBNIS — Christoph, 2026-08-23: „beide lampenausgänge getestet. funktioniert perfekt, kein
|
||
flackern."** Genau die Konstellation, die vorher geflackert hat, ist ohne AP stabil.
|
||
⇒ **`eco_mode` war die Ursache. Hypothese bestätigt, `s23` repariert.**
|
||
5. ✅ `s26 bad_luefter` analog: `eco_mode=false` gesetzt (`cfg_rev` 18 → 19) **+ Reboot** (s.u.).
|
||
🎯 **ERGEBNIS — Christoph: „lüfter perfekt."** Der Doppeldruck ist weg.
|
||
⇒ **Beide gemeldeten Geräte repariert, beide Symptome durch dieselbe Ursache erklärt.**
|
||
|
||
🔴 **Korrektur einer Annahme aus dieser Session:** ich hatte gewarnt, `eco_mode` brauche einen
|
||
**Neustart**, um zu wirken, und der Test sei deshalb unvollständig. **Falsch** — das Gerät antwortet auf
|
||
`Sys.SetConfig` selbst mit `{"restart_required": false}`. Der Test war richtig formuliert, ein Reboot
|
||
des Dimmers (= Lichtausfall in Diele UG/Wohnzimmer) wurde daraufhin **nicht** ausgeführt.
|
||
*(Und daraus folgt der wichtigere Schluss: `eco_mode` kann das `restart_required` der 14 Geräte unten
|
||
**nicht** verursacht haben.)*
|
||
|
||
#### Weitere Funde aus dem Sweep vom 2026-08-23 (read-only, alle 22 Geräte)
|
||
- 🔴 **Die eco-Politik ist damit nicht nur stale, sondern WIDERLEGT.** Regel von 2026-07-13 war
|
||
„eco **an** bei RSSI ≥ −80, **aus** darunter" — sie hat an `s23` **realen Alltagsschaden** erzeugt und
|
||
war nie konsistent umgesetzt. Ist-Stand des Sweeps 2026-08-23 (22 Geräte, live):
|
||
**eco AUS:** s7 (−90), s12 (−64), s17 (−76), s18 (−81), s19 (−88) · **eco AN:** alle übrigen 16,
|
||
darunter s20 (−85) und s21 (−85) — die RSSI-Regel ist an keiner Kante mehr erkennbar. Die Werte haben
|
||
sich seit Juli durch TP25/TP28 ohnehin verschoben. Die in TP17 vorgemerkte „2. Evaluation nach TP3.3"
|
||
hat nie stattgefunden.
|
||
⇒ ✅ **ENTSCHIEDEN UND AUSGEFÜHRT 2026-08-23. User: „eco-mode für alle deaktivieren. der rotz macht
|
||
nur probleme."** 13 Geräte umgestellt (s5, s6, s8, s10, s11, s13, s14, s15, s16, s20, s21, s22, s25),
|
||
**null Fehler**; 9 standen bereits auf `false`. **Stand jetzt: `eco_mode=false` auf allen 22
|
||
erreichbaren Shellys**, einzige Ausnahme `s24` (SNDC-0D4P10WW meldet `eco_mode: None` — nicht
|
||
unterstützt). Offen nur s1/s2/s3 (`weihnachten_*`, saisonal stromlos).
|
||
🔧 **Werkzeug:** `mcp/shelly/migrate.py eco <range> [--go|--on]` — neu, über denselben `_rpc`-Pfad,
|
||
mit Retry je Gerät (ein Timeout an einem schwachen Link darf den Sweep nicht abreißen) und einer
|
||
Fehlerliste am Ende.
|
||
|
||
🔬 **Und die Gegenmessung widerlegt meine eigene Warnung.** Ich hatte hier notiert, eco aus hebe die
|
||
**Sendeleistung** an und sei deshalb eine Airtime-Änderung, die TP25 berühre. **Gemessen stimmt das
|
||
nicht:** Signal der 13 geänderten Geräte in der Registration-Table des Managers, vorher/nachher —
|
||
**Mittel −0,9 dB, Median −1 dB, Spanne −3..+3**, bei einer **Kontrollgruppe von 13 unveränderten
|
||
Geräten mit +0,0 dB und derselben Streuung**. Das ist Rauschen, kein Effekt.
|
||
⇒ **Die Aussage aus TP17, `eco_mode` senke „den CPU-Takt UND die WLAN-Sendeleistung", stimmt nur zur
|
||
Hälfte.** Der Takt-Teil ist durch das Dimmer-Flackern bewiesen; der Sendeleistungs-Teil ist an diesen
|
||
Geräten nicht messbar. **Kein Funkbild-Argument mehr für oder gegen eco.**
|
||
- ✅ **AP-Anker-Sweep erledigt (der offene read-only-Punkt aus der Übergabe):** `WiFi.GetConfig →
|
||
ap.enable` über alle 22 Geräte. **Nur `s23` hatte einen AP an** (= Christophs Workaround, inzwischen
|
||
abgeschaltet), die übrigen 21 stehen auf `false`. **Kein Aufräumbedarf, kein weiterer Anker im Bestand.**
|
||
- 🔵 **Nebenbefund, gehört nicht hierher, aber nicht verlieren:** alle Shellys stehen auf
|
||
`sys.sntp.server = time.cloudflare.com`. Kanonisch ist seit 2026-08-18 **der Core als Zeitquelle**,
|
||
ausgeliefert per **DHCP-Option 42** — die Shellys haben aber aus TP17 **statische IPs** und sehen die
|
||
Option nie. Funktioniert (Uhrzeit stimmt, udp/123 aus VLAN30 geht raus), ist aber ein blinder Fleck in
|
||
der NTP-Vereinheitlichung. **Gleiche Klasse für jedes statisch adressierte Gerät im Netz.**
|
||
- 🔵 **`inventory/bf.csv` ist bei `s12` stale:** dort steht noch `10.40.41.219`, live ist das Gerät auf
|
||
`10.40.30.219` (migriert). Die drei `weihnachten_*` (s1–s3) sind saisonal stromlos, `s4
|
||
kueche_mikrowelle` offline — das erklärt 26 Einträge vs. 22 erreichbare.
|
||
- 🟠 **`restart_required = true` — nicht auf 2 Geräten, sondern auf 14 von 22** (Sweep 2026-08-23,
|
||
Folge-Session): s5, s6, s7, s10, s12, s13, s16, s17, s18, s19, s20, s21, s24, s26. Uptimes 22–41 Tage.
|
||
✅ **Entwarnung, empirisch:** `s26` wurde kontrolliert neu gestartet und die Config **davor/danach
|
||
verglichen** — sie ist **identisch**. Der Flag stand für **keine** abweichende Konfiguration; er ist
|
||
ein *sticky* Bit, das nur nie zurückgesetzt wurde. **Die Geräte sitzen nicht auf versteckten
|
||
Änderungen.** Reboots können damit gefahrlos gebündelt mit TP17.1 laufen.
|
||
- 🔍 **Was `restart_required` gesetzt hat, ist am Gerät NICHT rekonstruierbar** — drei Wege zu Ende
|
||
gegangen: (a) **169 RPC-Methoden** auf `s26`, keine gibt Auskunft über Pending-Zustand; (b) der
|
||
**Config-Diff über die Gruppengrenze trägt nicht** — die Gen3-Familie `S3PL-00112EU` hat *identische*
|
||
Configs, davon s6/s7/s10 flagged und s8/s11/s14/s15 sauber; (c) **kein persistentes Gerätelog**
|
||
(`sys.debug.file_log.enable=false`, `/debug/log` ist ein Live-Stream ohne Ring-Puffer).
|
||
Ausgeschlossen sind immerhin `matter.enable`, `ble.rpc.enable`, `cloud`, `mqtt`, `ws` (trennen die
|
||
Gruppen alle nicht) — und **`eco_mode`** (s.o.). *Nicht erneut untersuchen; der Aufwand ist gedeckelt
|
||
und das Ergebnis negativ.*
|
||
|
||
#### Scope / Abgrenzung
|
||
- **Gehört hierher:** alles, was die Migration am **Gerät** verstellt hat (eco_mode, statische IP,
|
||
AP-Anker) und was daraus an Fehlverhalten folgt.
|
||
- **Gehört nicht hierher:** die Funkumstellung selbst (**TP17.1**, verworfen) und das Geräte-Passwort
|
||
(seit 2026-08-24 **TP30 Posten #3**, vormals TP17.2).
|
||
- 🔗 **Aber die Verzahnung mit TP17.1 ist der Hebel:** wenn ohnehin **alle** Shellys angefasst werden,
|
||
ist das der Moment, `eco_mode` **grundsätzlich** zu entscheiden statt gerätweise zu pflegen.
|
||
Christophs „ich muss vermutlich eh alle shellys neu machen" ist damit nicht Resignation, sondern der
|
||
effiziente Weg — nur eben **nach** dem Test, nicht davor.
|
||
- **Governance:** Shellys laufen **nicht** über Nautobot GC (kein Netzgerät im GC-Sinn). Änderungen
|
||
über `mcp/shelly/` bzw. direkte RPC. ⚠️ `migrate.py` hat **kein** Kommando für `eco_mode` — der
|
||
Sweep-Weg wäre eine kleine Ergänzung über denselben Pfad.
|
||
- **Abh.:** keine. **Blockt nichts.** Der Alltagsschaden ist seit 2026-08-23 behoben.
|
||
|
||
#### 📌 Was noch offen ist (Stand 2026-08-23, Folge-Session)
|
||
1. ✅ Tastentest `s26` — **erledigt, Doppeldruck weg.**
|
||
2. **Flotten-Entscheid `eco_mode` aus** für die übrigen ~20 Geräte — **gebündelt mit TP17.1** wegen der
|
||
Funkbild-/Airtime-Wirkung (s.o.). Gehört Christoph.
|
||
3. **`migrate.py` um ein `eco`-Kommando ergänzen**, wenn 2. beschlossen wird — heute gibt es dafür
|
||
keinen Pfad im Werkzeug, der Sweep lief ad hoc.
|
||
4. Reboots der verbleibenden 13 `restart_required`-Geräte — **gefahrlos** (empirisch an `s26` belegt),
|
||
deshalb kein eigener Termin, sondern Mitnahme bei TP17.1.
|
||
|
||
### TP18 — Souveräner Objektspeicher (Garage, Multi-Site, S3-Cold-Tier) ▸ 🔜 KONZEPT (2026-07-12)
|
||
- **Auslöser:** 9-TB-Syno-Backup-Volume **zu 2/3 voll**; reine Daten ~3 TB, Tendenz steigend, „schlagartiger" Bedarf nicht ausgeschlossen. „Wenn skalieren, dann jetzt Syno-Nachfolger mitdenken."
|
||
- **Kriterien (User-priorisiert):** (1) **keine Fremd-Abhängigkeit / kein Cloud-Lock-in** (Cloud-Opex abschreckend); (2) **Multi-Site BF/FM/DW**; (3) **HA** (Standort-Verlust überlebbar); (4) **billig/teuer trennen** — Variante **„kalt = Objektspeicher, heiß = lokal"** genügt (kein Hot/Cold *innerhalb* des Stores nötig); (5) **horizontal + vertikal skalierbar**; (6) **S3-API als Branchenstandard**, den jedes Backup-Tool spricht.
|
||
- **Entscheid Engine = Garage** (Deuxfleurs, Rust): einziger Kandidat, der **geo-verteilt über offenes Internet/WAN latenztolerant** ist (CRDT-Metadaten, eventually consistent) → passt aufs 3-Keller-Modell BF/FM/DW. **Ceph verworfen** (will Sub-ms-LAN, Single-Cluster-über-WAN = Anti-Pattern; zu schwer, ~1 GB RAM/TB). **MinIO** (AGPL + Console/Feature-Abbau 2025) und **SeaweedFS** (kann Hot/Cold + Erasure-Coding, aber komplexer, Multi-Site async) = **Fallback**, falls später feingranulares Tiering / EC-Effizienz bei zweistelligen TB dominant wird.
|
||
- **Entscheid Disk-Layout = JBOD, KEIN RAID.** Redundanz gehört in **eine** Schicht: Garage RF=3 über 3 Sites *ist* die Redundanz. RAID 1 darunter = Doppelt-Bezahlen (3 Sites × 2 Mirror = **6× roh für 1× nutzbar**, absurd). RAID 0 verworfen (1 Platte tot = ganzer Node-Rebuild über WAN). **JBOD = jede Platte eigenes Garage-Data-Dir** (v1 Multi-HDD): Failure-Domain = 1 Platte, Rebuild nur deren Anteil, kein Kapazitätsverlust. **Merksatz: distributed store = „just a bunch of disks", Redundanz ausschließlich über die Sites.**
|
||
- **Was man dafür bezahlt:** Plattentod triggert **Re-Replikation über WAN** (durch Upload-BW gedeckelt). Bei ~3 TB/Site tolerabel; wenn Bedarf „schlagartig" auf zweistellige TB springt → mehr/kleinere Platten (Blast-Radius) oder 4. Node, ggf. Wechsel auf SeaweedFS-EC.
|
||
- **Dimensionierung (Planungsanker 10 TB nutzbar; heute 3 TB unique / 6 TB Backup-used):** RF=3 → **30 TB roh gesamt = 1 volle Kopie ~10 TB/Site** (provisionieren ~16 TB roh/Site: 2 Platten = halber Blast-Radius + Headroom). Linear skalierend.
|
||
- **Kostenmodell** *(Annahmen: HDD neu CMR ~€18/TB · recert ~€10/TB · Strom €0,25/kWh · Lean-Node Mini-PC/pve 12 W + 2 HDD ~20–28 W)*:
|
||
- **Capex kumulativ (3 Sites):** Platten €540 (recert) – €900 (neu) + Nodes €400 (BF=pve €0; FM/DW je Mini-PC ~€200) = **~€600–1.300**. Vergleich: *ein* resilienter Syno-Nachfolger für 10 TB = €1.000–1.600 capex **ohne** Geo-HA → Garage-3-Node gleich teuer/billiger **mit** HA.
|
||
- **Opex kumulativ:** ~€135–185/J Strom — aber **getrennt gerechnet:** FM/DW laufen über **fremde Zähler**, auf Christophs Zähler landet nur der **BF-Node ≈ €50–60/J** (Gegenleistung: er hostet deren Kopie, reziprok wie TP16).
|
||
- **JBOD-vs-RAID1-Ersparnis:** Strom bloß **€9–22/J** (Christophs Zähler) → *kein* Entscheidungsgrund; der Hebel ist **Capex ~€900** (nicht gekaufte Spiegelplatten) + architektonische Korrektheit. (Reicht nicht für ein BW-Upgrade €120–240/J, taugt aber als Seed für HW-Rückstellungen.)
|
||
- **Konzeptioneller Bruch (Syno-Nachfolger):** Kein *großes resilientes NAS* mehr nötig — die kollektive 3-Site-Redundanz *ist* die Garantie. **Syno-Rolle splittet:** Hot/SMB/VM-Disks bleiben lokal (Syno **oder** ZFS auf pve, klein/schnell); **Backup/Cold wandert in Garage über BF/FM/DW** → das 9-TB-Volume wird **aufgelöst** statt ersetzt (aus 1 SPOF werden 3 geo-redundante Kopien). Backup-Tool (restic/Kopia) sprechen S3 nativ.
|
||
- **Impacts auf Bestand (Kurz):** (a) **Storage-Redundanz-Philosophie kippt** — weg von SHR/RAID pro NAS, hin zu 3× JBOD; (b) **Backup-Kette** Hyper-Backup/SMB → S3-Bucket umstellen; (c) **Container-Host-System** (TP14) kriegt Garage als LXC/Container je Site, aber auf **rohen Platten**, nicht auf bestehenden SHR-Volumes; (d) **Upload-BW** wird Durchsatz-/Rebuild-Limit; (e) **HW-Planung** verzahnt mit TP15 (Node-HW, pve-Standort) und **TP24** (Stromversorgung je Site), FM/DW brauchen je 1 Node.
|
||
- **Startpfad (inkrementell/reversibel):** ① BF zuerst — Garage-Container auf pve, 2 HDDs JBOD, Backups lokal auf Bucket (sofort Nutzen, noch keine Redundanz). ② FM/DW nachziehen (Mini-PC + 2 Platten), RF→3, ab jetzt geo-redundant+HA. ③ Syno entlasten (Backup-Last abgeben).
|
||
- **Offen (Modell scharfstellen):** realer Strompreis; ob **FM/DW eigene Nodes stellen** oder Christoph HW hinstellt; Backup-Tool-Wahl (restic vs Kopia); ob heißes Zeug künftig auf ZFS/pve statt Syno.
|
||
- Abh.: **TP10** (WG-Multi-Site = Transport, Upload-BW-Constraint), **TP14** (Container-Hosts), **TP15** (Node-HW) / **TP24** (Stromversorgung), Muster von **TP16** (reziprokes Per-Host-HA). Kein Blocker für laufende TPs. SSoT dieser Session.
|
||
|
||
### TP20.1 — Vaultwarden → Platform-Postgres + Cross-Site-HA ▸ 🔜 KONZEPT (2026-07-14, ausgelagert aus TP20)
|
||
- **Ziel:** `vw` (`.20.127`, heute MariaDB) auf den Precious-Tier holen **und** das eigentliche User-Ziel erfüllen: **DB-Replikation über 2 Sites** (Standort-Verlust überlebbar).
|
||
- **Phase 1 — Migration (single-site):** vw **MariaDB→Postgres** (Export → Import in Tier-1-DB `vaultwarden` → `DATABASE_URL` umstellen → verifizieren). Danach ist vw precious + im pgBackRest/PITR-Schirm von TP20. *(Alternative verworfen: vw auf MariaDB lassen + MariaDB-Repl — zugunsten EINER Replikations-/Backup-Mechanik im Precious-Tier.)*
|
||
- **Phase 2 — Cross-Site-Standby:** `db-platform-standby`-LXC am 2. Standort → `pg_basebackup`/Streaming-Replication über **WG (TP10)** → **manuelles Promote-Runbook**. Repliziert den ganzen Tier-1-Cluster (vw = HA-Ziel, Nautobot bekommt Gratis-DR-Kopie). **Patroni + 3. Witness** nur, falls später Auto-Failover statt manuellem Promote gewünscht.
|
||
- **Verwandt/Abgrenzung:** vw ist auch in **TP16** offen (CF-Access: `bypass`→WARP-geschützt) — **DB-HA ≠ Access**, getrennt behandeln. Reziprokes 2-Site-Muster analog **TP16**/**TP18**.
|
||
- Abh.: **TP20** (Tier-1 Postgres muss stehen), **TP10** (WG-Transport für Phase-2), **TP18** (Backup-Schirm). Kein Blocker für TP21.
|
||
|
||
### TP23 — DNS-Policy & Self-Service-Ausnahmen ▸ ⏳ **STUFE 1 DURCH, STUFE 2 VORBEREITET (2026-08-18)**
|
||
- ✅ **Stufe 1 — DoH auf dem Core scharf** (`routeros/tp23-stufe1-doh-core.rsc`), schliesst zugleich
|
||
**TP21.1 TODO 1**. `use-doh-server=https://px35ynlol3.cloudflare-gateway.com/dns-query`,
|
||
**`verify-doh-cert=yes`**. `servers=1.1.1.1` bleibt als Bootstrap fuer den DoH-Hostnamen stehen.
|
||
- 🔑 **Die alte Begruendung „core hat kein CA-Bundle, verify also unmoeglich" war zu pessimistisch.**
|
||
Es braucht kein Bundle, sondern **genau einen Root**: die Kette ist
|
||
`Leaf → GTS WE1 → GTS Root R4`, importiert wurde nur **R4** (~1 KB, gueltig bis 2036) — bei
|
||
**2,8 MB freiem Flash** ist das der Unterschied zwischen geht und geht nicht.
|
||
Fingerprint lokal und am Geraet gegengeprueft (`349dfa40…3c7d`).
|
||
- 📌 Der Zertifikatsimport steht bewusst **nicht** im GC-Template: er braucht die Datei am Geraet
|
||
und ist einmaliger Bootstrap, kein Sollzustand. Im Template steht nur die `/ip dns set`-Zeile.
|
||
- ⚠️ **Der Deploy meldete FAILURE, obwohl alles sass.** `/certificate import` gibt `failed: 0` aus,
|
||
und der Job scannt den Geraete-Output stumpf nach dem Wort. Wieder ein Beleg fuer die
|
||
TP22-Regel: **Job-Status ist kein Wirksamkeitsnachweis**, nur die Gegenpruefung am Geraet zaehlt.
|
||
- ⏳ **Stufe 2 — AdGuard eingerichtet, aber bewusst NICHT scharf geschaltet** (User-Vorgabe 2026-08-18).
|
||
`adguard_bf1` (`10.40.20.16`) macht bereits DoH zum selben CF-Gateway. Ergaenzt wurden:
|
||
conditional upstreams **`[/bf.lan/]10.40.20.1`** und **`[/40.10.in-addr.arpa/]10.40.20.1`**
|
||
(ohne die zweite Zeile wuerden interne Reverse-Lookups zu Cloudflare abfliessen), sechs
|
||
**CIDR-Client-Gruppen** (ein VLAN = eine Gruppe) und die permanente Ausnahmegruppe
|
||
**`dns-bypass-exempt`** (aktuell `10.40.10.183`). Abnahme per `dig`: `core.bf.lan` → `10.40.99.1`,
|
||
`nas.bf.lan` → `10.40.20.10`, extern ueber DoH. Backup `AdGuardHome.yaml.bak-tp23-20260817`.
|
||
- **Kein DHCP-Scope wurde angefasst** — alle sieben liefern weiter `10.40.X.1` aus. Das
|
||
Scharfschalten ist damit spaeter ein reiner DHCP-Change; die passende Firewall-Zeile
|
||
(`TP5-E2: LAN->AGH dns (Vorrat)`) steht bereits, damit dabei die scharfe Firewall nicht
|
||
angefasst werden muss.
|
||
- ⚠️ **AGH schreibt die YAML beim Start neu** (eigene Schluesselreihenfolge, Kommentare weg,
|
||
`time_zone: Local` → `UTC`). Wer die Datei patcht, muss danach am **laufenden** Stand pruefen,
|
||
nicht am eigenen Entwurf.
|
||
- 🔜 **Offen bleiben Stufe 3–6** (zweite AGH-Instanz, per-Lease Option 6, HA-Buttons, und zuletzt die
|
||
Bypass-Sperre). **Ausdruecklich keine HA/zweite Instanz** (User 2026-08-18).
|
||
- **Auslöser:** sobald gefiltertes DNS scharf ist, gehen einzelne Seiten/Dienste kaputt. Es braucht **Ausnahmen ohne Admin-Eingriff** — per User/Client/Gruppe/global, temporär oder permanent, mit Oberfläche. **Bedarf ist belegt, nicht vermutet:** der bisherige Workaround war „Client von Hand auf `1.1.1.1` stellen", regelmäßig (User 2026-08-14).
|
||
- 🔴 **Zentrale Reihenfolge-Regel: genau dieser Workaround IST der DNS-Bypass, den TP5 schließen will → erst der Ersatz, dann die Sperre.** Ohne funktionierende Ausnahme-Oberfläche verliert u.a. der Entwicklungsrechner samt Agenten-Toolchain seinen Ausweg — und DNS-Blocks äußern sich dort nicht als klare Meldung, sondern als diffus kaputte Build-Umgebung.
|
||
- **Zwei Befunde, die die Architektur bestimmen:**
|
||
1. **DoH ist nicht das Problem.** Clients sprechen plain mit dem lokalen Resolver, der macht DoH nach oben — die empfohlene Auflösung des DoH/Split-Horizon-Konflikts, `bf.lan` bleibt heil. Was Seiten blockiert, ist die **Policy**. ➡️ **Nie einen „DoH aus"-Schalter bauen**, sondern Policy-Ausnahmen.
|
||
2. **Identität existiert nur dort, wo die Client-IP sichtbar ist.** Router-DoH aggregiert alle Clients hinter *einer* Quelle — CF sieht nur den Core. Per-User-Identität *bei CF* verlangt einen **Agent** (WARP; bei NextDNS/ControlD deren CLI) → auf Shelly/Sonos/TV unmöglich. **RouterOS kann keine per-Client-Ausnahme** (keine DNS-Views; `/ip dns static` wirkt domain-weit — verifiziert). ➡️ Ein lokaler Resolver mit Client-Policy ist unvermeidbar.
|
||
- ✅ **Architektur-Entscheid: Filter zentral bei CF, Identität + Ausnahmen lokal.** Die **Filterliste bleibt vollständig bei Cloudflare** (Core via DoH, unterwegs via WARP); lokal wird **nie eine Filterliste vorgehalten, nur die Diffs**. Vorteile: kein RAM-Thema auf dem Core (eine Adlist kostet ~1,9 KB/Domain, 50k ≈ 95 MB — Konkurrenz mit Conntrack/DHCP/WG), **eine** Policy-Quelle statt zweier, gleiche Regelbasis zuhause wie unterwegs.
|
||
- ✅ **Resolver-Wahl: AdGuard Home** (läuft bereits, `10.40.20.16`). Vergleich gegen die realen Anforderungen:
|
||
- **Pi-hole disqualifiziert:** **spricht kein DoH** → bräuchte cloudflared/unbound als Sidecar (vierte QUIC-Komponente neben den drei TP16-Connectoren). Dazu belegtes Bruchrisiko **v5→v6** (FTL/API/Web neu, Docker-Env-Variablen tot).
|
||
- **AGHs „keine Gruppen"-Schwäche verschwindet hier**, weil Clients als **CIDR** eintragbar sind → `10.40.30.0/24` = die IoT-Gruppe. **Die VLAN-Segmentierung ist bereits die Policy-Grenze.** Dazu natives DoH, per-Device-ID auch bei wechselnder IP, „Unblock for this client", API + HA-Integration.
|
||
- **Technitium** = ernsthafte Alternative (autoritativer DNS + DHCP + Apps, `networkGroupMap`); seine IP/Subnet-Bindung trifft uns kaum, weil die IPs per Reservierung faktisch statisch sind. **Neubewertung legitim** (die TP4-Ablehnung galt einer anderen Frage) — sinnvoller Anlass wäre, DNS **und** DHCP zusammenzuführen. Eigener TP, nicht dieser.
|
||
- **Verworfen:** eigene Self-Service-UI bauen · per-Device-DoH via NextDNS/ControlD (Agent-Zwang, bricht `bf.lan`) · Umbrella ($30–45/User/Jahr) · Cloudflare verlassen (wirft TP16/TP16.1 weg) · Adlist auf dem Core.
|
||
- **Zuweisung ist nativ möglich:** `/ip/dhcp-server/lease` hat die Property **`dhcp-option`** (verifiziert) → **Option 6 pro Lease**, bei 54 Reservierungen ein Konfigurationsschritt. *(Nebenfund: Leases haben `address-lists` → automatische Aufnahme in eine Firewall-Liste, direkt nützlich für die TP5-JIT-Gruppe.)*
|
||
- ✅ **WARP im LAN abschalten — Mechanismus: Cloudflare „Managed Networks".** Der Client sucht bei jedem Netzwechsel einen **selbst gehosteten TLS-Endpunkt**, prüft dessen SHA-256-Fingerprint und zieht ein Device-Profil, das WARP **deaktiviert**. Beacon auf **NAS/pve**, nicht auf den Core (`certs=0`). Beseitigt die ICMP-Falle vom 2026-08-13 (`ref_warp_local_icmp_trap`) an der Wurzel.
|
||
- ✅ **Kollision aufgelöst (2026-08-14):** die frühere Sorge war, dass TP16-Schritt 5 `vw` auf WARP-geschützt hebt und damit „WARP zuhause aus" bricht. **Der User-Constraint aus TP16.2 streicht genau das** — `vw` bleibt bypass und verlangt nie Device-Identity. **Damit steht der Managed-Networks-Weg frei.** Rest-Risiko nur für Dienste, die künftig doch Device-Identity verlangen sollten → dann Access-Policy zusätzlich auf die Heim-IP (`bf4-home-ip` existiert bereits).
|
||
- ✅ **Kein Verlust beim Fremdnetz-Zugriff:** FM/DW laufen über den WG-Hub (TP10), nicht über WARP. Managed Networks schaltet nur *im* Heimnetz ab.
|
||
- ⚠️ Free-Plan-Verfügbarkeit des Features **nicht belegt** → im Dashboard gegenprüfen. Holzhammer-Alternative (WARP-Endpunkte per Firewall sperren) **nicht empfohlen**: bricht still, Client probiert Transport-Fallbacks, trifft auch Gäste.
|
||
- **Umsetzung in Stufen — Sperre zuletzt:**
|
||
1. **DoH auf dem Core scharf** → schließt TP21.1-TODO 1 (heute plain `1.1.1.1`); CA-Bundle nötig.
|
||
2. **WARP-Managed-Network** (Beacon auf NAS/pve) → WARP zuhause aus.
|
||
3. AGH in den Pfad, **zweite Instanz** + Config-Sync, beide per DHCP als DNS 1 + 2.
|
||
4. **Client-Gruppen per CIDR** (ein VLAN = eine Gruppe); per-Lease Option 6 nur für Abweichler.
|
||
5. HA-Buttons für temporäre Freigaben + JIT (AGH-API, RouterOS-REST, CF-API), RouterOS-`scheduler` als Backstop, damit Ablauf auch ohne HA greift.
|
||
6. 🔴 **Erst jetzt (in TP5): DNS-Bypass sperren** — Vorbedingung: Freigabe über HA funktioniert **nachweislich**, und die permanente Ausnahmegruppe **`dns-bypass-exempt`** (Entwicklungsrechner + Build-/Agent-Tooling) steht. Dann: nur Resolver dürfen 53/853 nach außen (schließt Geräte, die eigene Resolver hardcoden — Smart-TV mit `8.8.8.8` ist der Klassiker).
|
||
- **Grenzen, offen benannt:** **ein Resolver im Datenpfad** ist unvermeidbar (zwei Instanzen federn ab, ⚠️ **AGH hat kein natives HA → Config-Sync ist Betriebsaufwand**, sonst gilt nach Failover eine andere Policy) · **keine Blockseite bei HTTPS** (gilt für jede DNS-Filterlösung, auch Umbrella) · **CF-Log-Retention 24 h** im Free-Plan — laut User ausreichend, weil Ausnahmen JIT entstehen und ältere Fälle reproduziert werden.
|
||
- ⚠️ **802.1X löst die IoT-Identität NICHT:** Consumer-IoT (Shelly, Sonos) hat keinen 802.1X-Supplicant; der Standardweg ist **MAB** = wieder MAC-basiert → **kein Identitätsgewinn gegenüber der DHCP-Reservierung**. **TP8 bleibt richtig, aber als Sicherheitsthema** (dynamische VLAN-Zuweisung, kein MAC-Spoofing bei Managed-Geräten), nicht als DNS-Identitätsquelle.
|
||
- **Abh.:** **TP21.1 TODO 1** (DoH auf core), **TP5** (Firewall/Bypass-Sperre — Reihenfolge s.o.), **TP16 Schritt 5** (Access-Kollision), **TP16.1** (falls WARP zuhause bleiben soll), **TP8** (nur Sicherheit). **Kein Blocker für TP5 → TP6**, aber TP5 darf die Bypass-Sperre nicht vorziehen.
|
||
|
||
### TP24 — Unterbrechungsfreie Stromversorgung (DC-USV, 48V-Bus) ▸ 🔜 KONZEPT — **aus TP15 herausgelöst (User-Entscheid 2026-08-16)** — `docs/hw-refresh-requirements.md §9`
|
||
- **Warum ein eigener TP:** Die USV hing bisher als Unterpunkt in TP15 (HW-Refresh Switching/Router) mit. TP15s Kernauftrag — CRS326-Beschaffung, Bring-up, Core-Tausch — ist seit 2026-08-08 **durch**; die Stromversorgung ist als einziger Rest übrig geblieben und hat mit Switching sachlich nichts zu tun. Sie ist ein **eigenständiges Infrastruktur-Thema mit eigener Einkaufs-, Verkabelungs- und Entscheidungslogik** (Elektrik, Batterie, Standort) und blockiert nichts auf dem kritischen Pfad. Darum eigener TP.
|
||
- **Was es ist (Begriffsklärung):** eine **Pufferbatterie für die Netz-/Infrastruktur-Hardware** — **keine** klassische AC-USV mit 230-V-Steckdosen. Ein 48-V-Gleichspannungsbus versorgt die Geräte **direkt an ihrem DC-Eingang**; die Batterie hängt am selben Bus und übernimmt bei Netzausfall ohne Umschaltlücke. Der Umweg AC→DC→AC→DC (und damit dessen Dauerverlust) entfällt.
|
||
- **Zielbild (Aufbau, unverändert gültig aus der TP15-Recherche):**
|
||
|
||
| Teil | Typ | Preis | Wirkungsgrad |
|
||
|---|---|---|---|
|
||
| Netzteil | MeanWell **LRS-150-48** (150 W, 48 V) | ~€30 | ~88 % |
|
||
| DC-USV-Modul | MeanWell **DDR-120-48** (120 W) | ~€80 | ~95 % (pass-through) |
|
||
| Batterie | **LiFePO4 12,8 V / 20 Ah** (256 Wh) | ~€50 | — |
|
||
| DC-DC 12 V | MeanWell SD-100A-12 (100 W, für NAS/pve) | ~€30 | ~85 % |
|
||
| **Summe** | | **~€190** | |
|
||
|
||
`230 V AC → LRS-150-48 → 48-V-Bus →` Switches direkt · `→ DC-DC 12 V →` DS923/pve · `→ DDR-120-48 →` LiFePO4.
|
||
- **Warum 48 V:** PoE-Technik arbeitet nativ mit 44–57 V (kein Wandler nötig), höhere Spannung = kleinere Ströme = weniger Leitungsverlust, und 48 V ist der Telecom-/USV-Standard.
|
||
- **Laufzeit (256 Wh):** ~2,5 h bei 100 W reiner DC-Last · **~5,5 h wenn nur Switch + PoE (45 W)** · ~2 h wenn ein Inverter für den Mac mini mithängt.
|
||
- **Effizienz-Argument (der eigentliche Grund für DC statt AC):** eine AC-USV verheizt bei 100 W Dauerlast ~25–40 W (≈ €66–105/Jahr), die DC-Variante ~5–12 W (≈ €13–32/Jahr) — bei praktisch gleicher Anschaffung (~€190 vs. €150–250). Der Mehrpreis amortisiert sich über den Dauerbetrieb.
|
||
- ✅ **DC-Eingang vorhanden (geprüft):** `crs326-core` + `crs326-branch` (12–57 V, DC-Jack) · ap1/ap2 hAP ax² (12–57 V, alternativ PoE-in). ⚠️ **Adapter/Sonderfall:** DS923+ (externes 12 V/7 A, 4-Pin Mini-Fit JR) · pve/N100 (12 V Hohlstecker) · **Mac mini hat gar keinen DC-Eingang** (eingebautes Netzteil → nur über Inverter).
|
||
- **Offene Entscheidungen (aus TP15 mitgenommen, unverändert offen):**
|
||
1. **Mac mini auf USV?** Braucht einen Inverter (~15–20 % Verlust + Komplexität). Vorfrage: läuft er überhaupt 24/7? Wenn nein, reicht ein Graceful-Shutdown-Script auf USV-Warnung.
|
||
2. **Standort/Verkabelung:** Der Bus soll laut Skizze im **Arbeitszimmer/Technik OG** sitzen — der L3-Core steht aber in **Technik UG**. Entweder zweiter Zweig nach unten oder Bus dorthin, wo der Core steht. **Diese Frage ist durch den Core-Umzug neu zu stellen** (die Skizze stammt aus der Zeit, als oben der Verteiler geplant war).
|
||
3. **Arris-Kabelmodem:** vorentschieden auf **Grid** (muss ohnehin am Netz bleiben, DC-Zweig nach unten wäre Aufwand für ~1–2 W).
|
||
4. **Batteriegröße:** 20 Ah als Startwert; 50 Ah wenn die gepufferte Last über Switch+AP hinaus wächst.
|
||
5. **Monitoring/Shutdown:** wie meldet die USV einen Netzausfall an NAS/pve (Signalkontakt vs. nichts)? Ohne das gibt es kein sauberes Herunterfahren, nur Durchhalten bis die Batterie leer ist.
|
||
- **Nutzen konkret:** bei Stromausfall bleiben Core-Switch, Branch-Switch und beide APs online → **Netz, WLAN und der WG-Hub überleben** (der WAN-Uplink nur, solange auch der Arris Strom hat, s. Entscheidung 3). Für NAS/pve zählt vor allem das **kontrollierte Herunterfahren** statt des harten Abschaltens.
|
||
- **Abh.:** keine harten. **TP15 ✅** (die zu versorgende Hardware steht und hat DC-Eingänge). Verwandt: **TP18** (Garage-Nodes brauchen je Site eine Stromversorgung), **TP11** (USV-Zustand ins Monitoring). **Kein Blocker für TP5/TP6.**
|
||
- **Historie:** war bis 2026-08-16 Unterpunkt von TP15 („🔋 DC-USV: MeanWell LRS-150-48 + DDR-120-48 + LiFePO4 20Ah (~€190), 48V-Bus; CRS326 DC-input passt"). Vollständige Recherche inkl. Schaltbild, Effizienzvergleich und Gerätetabelle: `docs/hw-refresh-requirements.md §9` (+ §10 D/E/F für die offenen Fragen).
|
||
|
||
### TP25 — WLAN-Stabilisierung ▸ 🔬 **FUND E OFFEN, HEBEL 1 LÄUFT (2026-08-31 08:09): `ft-over-ds=no` auf `sec-main`, Messung bis 2026-09-01** · Kern erledigt: 🟢 **(2026-08-22) Dead Spot Terrasse repariert** (43 % → 9 % Verlust, 20,4 s → 1,0 s). **Fund F ✅ aufgelöst (08-24, geräteseitig — kein Netzfehler)**, **5-GHz-Umfeldscan ✅ (08-24)**, Kanalplan für AP 3 ✅ gerechnet. Offen nur noch die Umsetzung, wenn der dritte AP kommt. Nicht mehr kritischer Pfad — Sammel-TP für Christophs Beobachtungen 1–6
|
||
> 📚 **Kondensiert am 2026-08-23 (Doku-Session, Punkt F).** Dieser Abschnitt war mit 394 Zeilen
|
||
> 18 % der Datei. Die **Langfassung steht vollständig und unverändert** im Archiv:
|
||
> [`teilprojekte-archiv.md` → TP25 (Langfassung)](teilprojekte-archiv.md#tp25-lang) — Messchronik (A)–(G),
|
||
> Funde A–E im Volltext, die beantworteten Fragen (Beobachtung 5, ch149-Herkunft, `2ghz-g`,
|
||
> Deckenmontage), Umsetzungsreihenfolge mit allen Deploy-Gotchas. Hier steht: **Stand, Offenes,
|
||
> Merkregeln.** Wer an TP25 weiterarbeitet, kommt mit diesem Abschnitt aus; wer wissen will
|
||
> *warum* etwas so entschieden wurde, liest die Langfassung.
|
||
|
||
**Stand 2026-08-22, 23:30 — die Terrasse ist repariert.** Erster Lauf mit beiden Ping-Strecken
|
||
([`tp25-runde-20260822-2330-auswertung.md`](tp25-runde-20260822-2330-auswertung.md)): **43 % Verlust /
|
||
20,4 s längste Lücke (08-21) → 9 % / 1,0 s** bei gleichem Gerät und gleicher Rolle. Wirksam war das
|
||
**Anheben von ap2 auf 2,5 m**; 1,0 s liegt unter der Hörbarkeitsschwelle im Call. Der Dead Spot, der
|
||
TP25 seit dem 08-20 getragen hat, ist damit weg.
|
||
|
||
🔎 **Kernbefund, der bleibt: das Netz ist gut, die Redundanz ist es nicht.** Die frei roamende Referenz
|
||
hat in **17 von 21** Raumabschnitten 0 % Verlust; der Gegen-AP liefert Schlafzimmer −84,6/38 %,
|
||
Bad −82,3/24 %, Küche −82,0/23 %, Terrasse −78,7/19 %. **Fällt ein AP aus, ist die halbe Wohnung
|
||
„verbunden, aber tot".** ⇒ Ein **dritter AP ist nach Christophs eigener Randbedingung derzeit NICHT
|
||
begründet** — er kaufte Redundanz, keine Abdeckung. Das ist eine andere Frage und Christophs.
|
||
|
||
**Beide vormals offenen Punkte sind am 2026-08-24 abgearbeitet** — Fund F ist aufgelöst, der
|
||
5-GHz-Umfeldscan gefahren und der Kanalplan für einen dritten AP gerechnet. **Was von TP25 bleibt,
|
||
ist Umsetzung bei Bedarf, keine offene Frage:**
|
||
1. ✅ **Fund F — GEMESSEN UND AUFGELÖST (2026-08-24): kein netzseitiges Phänomen.**
|
||
Kollektor am **Manager** (`/interface wifi registration-table`, 15-s-Takt, 29 Samples über 7,7 min,
|
||
28 Clients), Rohdaten `docs/messdaten/tp25-fundf-20260824.log`. Von den drei Kandidaten sind **zwei
|
||
erledigt, einer bleibt** — und die zwei fielen ohne Wartezeit:
|
||
|
||
| Kandidat | Ergebnis | woran es hängt |
|
||
|---|---|---|
|
||
| **(b)** DHCP-`lease-time` gegen die Periode | ❌ **tot** | `dhcp-v30` fährt **`lease-time=1d`** → Renew bei 12 h. Kann 5:05 nicht erzeugen. **Eine Abfrage, keine Messung.** |
|
||
| **(a)** periodischer VAP-Re-Apply | ❌ **tot** | Ein Re-Apply wirft **alle** Clients des VAP gleichzeitig. Auf `cap-wifi4-virtual1` standen zeitgleich **drei Clients bei 16 h 51 min** Uptime, 25 von 28 über 6 h. Mit einem 5-Minuten-Takt ist das unvereinbar. |
|
||
| **(c)** das Gerät selbst | 🟡 **bleibt** | Der einzige Client, der im Fenster überhaupt neu verband. |
|
||
|
||
🔑 **Der entscheidende Punkt an der Beweisführung: dafür braucht es die vier Zyklen nicht.** Ein
|
||
einziges Sample mit **16 h 51 min Uptime auf genau dem VAP**, der angeblich alle 5:05 abwirft,
|
||
widerlegt (a) vollständig. Die Fensterlänge begrenzt nur, was man über *abwesende* Geräte sagen kann.
|
||
|
||
🔴 **Und genau die sind abwesend: die beiden Fund-F-MACs gibt es gerade nicht.**
|
||
`38:A5:C9:C0:73:1E` (`10.40.30.178`) und `38:A5:C9:BC:33:31` (`10.40.30.179`) stehen in `/ip arp` auf
|
||
**`status=failed`**, haben **keine DHCP-Lease**, stehen **nicht** in der Registration-Table und sind
|
||
**auch HA unbekannt** (kein `device_tracker` zu diesen MACs). ⇒ **Das Phänomen ist an diesen Geräten
|
||
derzeit nicht reproduzierbar, weil die Geräte weg sind.**
|
||
📌 **Merkregel — dieselbe wie beim P750W:** bevor ein periodisches Trennen als Funk- oder
|
||
Konfigurationsfehler untersucht wird, **erst prüfen, ob die betroffenen Geräte überhaupt da sind.**
|
||
Beim Drucker war die Fehldiagnose „Power-Save", hier wäre sie „VAP-Re-Apply" geworden.
|
||
|
||
⇒ **Fund F wird geschlossen:** was auch immer der 5:05-Takt war, er lag **geräteseitig**, und die zwei
|
||
betroffenen Geräte sind vom Netz. Netzseitig ist nichts zu tun. **Nicht erneut aufgreifen**, solange
|
||
`.30.178`/`.179` nicht wieder da sind *und* das Muster erneut gemessen wird.
|
||
|
||
🟡 **Nebenbefund mit Substanz — der DTU ist der einzige Wackelkandidat, den es wirklich gibt.**
|
||
`54:F2:9F:12:51:F0` (DTU/Wechselrichter, auf `cap-wifi4-virtual1` = **ap1**) war der **einzige** Client
|
||
mit einem Uptime-Rücksetzer: 43 min Sitzung, dann weg für ein Sample, dann wieder da — **bei −52 dBm**,
|
||
also bei tadellosem Signal. Das ist derselbe Mechanismus, der schon die Transaktion
|
||
`TP17.1-dtu-pin-ap2` begründet hat: **ein ESP hat EIN Radio, sein eigener AP und die STA-Verbindung
|
||
teilen sich den Kanal.** Periode hier 43 min, nicht 5:05 — es ist **nicht** Fund F, aber es ist echt.
|
||
⇒ Gehört zur ohnehin vorgesehenen Neubewertung des DTU-Pins, sobald der Kanalplan steht (s.o.).
|
||
|
||
<details><summary>📜 Ursprüngliche Fund-F-Formulierung (2026-08-22)</summary>
|
||
|
||
1. 🟡 **Fund F — IoT-Clients trennen exakt alle ~5:05 bei gutem Signal** (−59 dBm, vier Messpaare,
|
||
auf 3 s reproduzierbar → **ein Timer, kein Funk**). Muss **am Manager** nachgemessen werden, die
|
||
AP-Tabellen sind unter CAPsMAN leer. Kandidaten und der Test, der sie trennt:
|
||
(a) periodischer VAP-Re-Apply (Fund D war die Ursache → prüfen, ob es nach dem Fix weg ist);
|
||
(b) DHCP-`lease-time` der beiden MACs (`38:A5:C9:C0:73:1E`, `38:A5:C9:BC:33:31`) gegen die Periode legen;
|
||
(c) das Gerät selbst mit 5-min-Duty-Cycle → dann bleibt es unverändert.
|
||
🚫 **Nicht die alte Power-Save-Merkregel wiederbeleben** — die ist am 2026-08-13 als Fehldiagnose
|
||
zurückgezogen (`ref_wifi_powersave_multicast`).
|
||
|
||
</details>
|
||
|
||
2. ⚖️ **Entscheidung 5 GHz auf 20 MHz** (statt heute 20/40) — **✅ 5-GHz-Umfeldscan durchgeführt
|
||
2026-08-24, Empfehlung: NICHT verschmälern, Entscheidung an einen Auslöser hängen.**
|
||
⚠️ **2,4 GHz NICHT vorschnell drosseln** — dort hängen ~20 IoT-Geräte.
|
||
|
||
**Messung** (`/interface wifi scan` **am Manager**, cap-wifi3 = ap1 / cap-wifi1 = ap2, 10–12 s):
|
||
|
||
| vom Radio auf | fremdes 5-GHz-Netz | Kanal | Signal | Abstand zum eigenen AP |
|
||
|---|---|---|---|---|
|
||
| **ap1** (5180/Ce) | `Drei_H288A_5G_acHG` | 5745/ac/**80 MHz** | **−86 dBm** | 19 dB darunter |
|
||
| **ap2** (5220/Ce) | `LiwestBB93-5G` | 5200/ac/**80 MHz** (Block 5180–5240) | **−89 dBm** | 24 dB darunter |
|
||
|
||
⇒ **Das 5-GHz-Band ist an beiden Standorten praktisch leer.** Genau **ein** Fremdnetz je AP, beide
|
||
rund 20 dB unter dem eigenen Pegel. Der Nachbar `LiwestBB93-5G` liegt mit 80 MHz zwar formal über
|
||
**beiden** unserer Blöcke — bei −89 dBm ist das ohne Wirkung.
|
||
|
||
🎯 **Warum das gegen 20 MHz spricht — die zwei üblichen Gründe treffen hier nicht zu:**
|
||
- **Fremdinterferenz:** existiert nicht (−86/−89). Ein schmalerer Kanal hätte nichts abzuwehren.
|
||
- **Selbstüberlappung:** existiert nicht. ap1 `5180/Ce` = ch36+40, ap2 `5220/Ce` = ch44+48 —
|
||
überlappungsfrei (Fund C, 08-17).
|
||
- **Bleibt allein das +3 dB Link-Budget** — und das ist zum guten Teil **schon vorhanden**: in einem
|
||
40-MHz-BSS sendet die Ratenanpassung an schwache Clients ohnehin auf dem Primärkanal in 20 MHz.
|
||
Den Ceiling zahlen aber **alle** Clients dauerhaft. **Der Nutzen geht an die, die ihn schon haben.**
|
||
- **Und es löst das gemessene Problem nicht:** die offenen Schwachstellen sind die
|
||
Redundanz-Lücken (Schlafzimmer −84,6 / 38 %, Bad −82,3 / 24 %) **am Gegen-AP**. +3 dB macht aus
|
||
38 % Verlust keine 0 % — das ist eine Platzierungs-/AP-Zahl-Frage, keine Kanalbreiten-Frage.
|
||
|
||
🔑 **Der echte Auslöser ist die AP-Zahl, nicht die Interferenz** — und das ist der Grund, die
|
||
Entscheidung *jetzt* zu treffen statt sie offenzulassen. Beide Kanäle stehen hart in **Band A**
|
||
(`ch-5g-ap1`=5180, `ch-5g-ap2`=5220, kein `skip-dfs-channels`), und Band A hat unter ETSI genau
|
||
**4 × 20 MHz = 2 × 40 MHz**. Mit zwei APs ist es damit **exakt aufgebraucht**:
|
||
|
||
| Szenario | bei 40 MHz | bei 20 MHz |
|
||
|---|---|---|
|
||
| 2 APs (heute) | ✅ passt genau, überlappungsfrei | ✅ passt, +3 dB, halber Ceiling |
|
||
| **3.–4. AP** | ❌ Band A voll ⇒ **nur mit DFS** (Band B ch52–64: Radar-Events, 60 s CAC) | ✅ 4 saubere Kanäle, **kein DFS** |
|
||
|
||
⇒ **Empfehlung: 20/40 MHz beibehalten.** Die Verschmälerung ist heute ein Preis ohne Gegenwert.
|
||
Sie wird **fällig, sobald ein dritter AP dazukommt** — dann lautet die Wahl nicht mehr „20 gegen 40",
|
||
sondern **„20 MHz flottenweit" gegen „DFS akzeptieren"**, und dafür ist 20 MHz die ruhigere Antwort.
|
||
Ein Zwischenweg existiert (ap1 behält 40 MHz, AP 2+3 fahren 20 MHz auf 5220/5240) und braucht
|
||
ebenfalls kein DFS.
|
||
📌 **Damit ist dieser Punkt entscheidungsreif und an einen Auslöser gebunden statt offen.**
|
||
|
||
#### 📐 Kanalplan für den dritten AP ▸ **gerechnet 2026-08-24, DFS vom User genehmigt**
|
||
> **User-Vorgaben (2026-08-24):** 20/40 MHz bleibt · **DFS ist genehmigt**, soll aber auf **ap1 oder ap2**
|
||
> liegen, nicht auf dem Neuen · der Neue ist ein **MikroTik-Deckenstrahler mit ax** · **möglicherweise
|
||
> kommt er an die Stelle eines der bisherigen APs** · der vorhandene Outdoor-AP bleibt konzeptionell außen vor.
|
||
|
||
**Das Kanalbudget unter ETSI (Indoor), als Rechnung statt als Gefühl.** Ein 40-MHz-Block darf keine
|
||
Bandgrenze überschreiten — `[48+52]` ist deshalb unmöglich, und daran hängt die ganze Knappheit:
|
||
|
||
| Band | Frequenzen | Kanäle | 40-MHz-Blöcke | DFS | EIRP |
|
||
|---|---|---|---|---|---|
|
||
| **A** | 5150–5250 | 36/40/44/48 | **2** (`5180/Ce`, `5220/Ce`) | nein | 23 dBm |
|
||
| **B** | 5250–5350 | 52/56/60/64 | **2** (`5260/Ce`, `5300/Ce`) | **ja** + TPC | 23 dBm |
|
||
| **C** | 5470–5725 | 100–140 | **5** | **ja** + TPC | 30 dBm, outdoor erlaubt |
|
||
|
||
Heute belegen zwei APs mit 40 MHz **ganz Band A**. Genau das macht den dritten AP zur Bandfrage — und
|
||
mit genehmigtem DFS ist sie lösbar, ohne irgendwo auf 20 MHz zu gehen.
|
||
|
||
**Empfohlene Zuordnung (3 APs, alle 40 MHz):**
|
||
|
||
| AP | Rolle | Kanal 5 GHz | Band | DFS | 2,4 GHz |
|
||
|---|---|---|---|---|---|
|
||
| **cAP ax (neu)** | Primär, beste Position | **`5180/Ce`** (36+40) | A | **nein** | 2437 (ch6) |
|
||
| **ap1** hAP ax² | Sekundär | **`5260/Ce`** (52+56) | B | ja | 2412 (ch1) |
|
||
| **ap2** hAP ax² | Sekundär | **`5300/Ce`** (60+64) | B | ja | 2462 (ch11) |
|
||
| — | **Ausweichspur, frei halten** | `5220/Ce` (44+48) | A | — | — |
|
||
|
||
- **Warum Band B und nicht Band C für die zwei DFS-Radios:** gleicher 23-dBm-Deckel wie Band A (kein
|
||
Leistungssprung zwischen den APs) und **CAC 60 s statt 600 s** — die 10-Minuten-Prüfung gilt im
|
||
Wetterradar-Bereich von Band C. Band C bleibt damit frei für den Outdoor-AP, wo seine 30 dBm auch
|
||
hingehören.
|
||
- **Warum der Neue das nicht-DFS-Band bekommt:** DFS kostet **60 s CAC bei jedem Boot und jeder
|
||
Provisionierung** plus einen erzwungenen Kanalwechsel bei Radar-Erkennung. Diese Kosten gehören auf
|
||
die Radios, deren Fläche von einem anderen AP mitgetragen wird — nicht auf den bestplatzierten.
|
||
Christophs Instinkt ist hier richtig.
|
||
- **2,4 GHz fällt nebenbei sauber:** ch1/6/11 ist mit drei APs erstmals vollständig überlappungsfrei
|
||
möglich (heute 1/11 mit einer Lücke in der Mitte).
|
||
|
||
🔴 **Zwei Fallen, ohne die der Plan teurer wird als er aussieht:**
|
||
1. **Eine hart gepinnte Frequenz macht DFS zum Single Point of Failure.** `ch-5g-ap1`/`-ap2` tragen heute
|
||
**je genau einen** Wert (`frequency=5180` bzw. `5220`). Bei einer Radar-Erkennung **muss** das Radio
|
||
den Kanal verlassen — hat es keinen zweiten konfiguriert, bleibt das 5-GHz-Funk dieses APs **unten**.
|
||
⇒ Für DFS gehört in das Channel-Objekt eine **Liste** (z.B. `frequency=5260,5280,5220`), damit der AP
|
||
neu wählen kann. Das ist der Unterschied zwischen „DFS ist unkritisch" und „DFS kostet einen AP".
|
||
2. **Vor dem Umzug die *erreichte* Sendeleistung messen — das ist Fund B, nochmal.** Band B/C verlangen
|
||
TPC; ohne TPC sinkt der erlaubte EIRP um 3 dB. TP25 Fund B war exakt dieses Muster: **18 dBm gesetzt,
|
||
9 dBm gesendet**, weil ch149 unter ETSI leistungsbegrenzt ist. ⇒ **Diskriminierender Test, bevor
|
||
irgendwas gekauft oder umgezogen wird:** ap2 testweise auf `5260`, dann `/interface wifi monitor`
|
||
lesen und die tatsächliche `tx-power` gegen die heutigen 18 dBm auf `5220` stellen. Kostet eine
|
||
Config-Transaktion und zehn Minuten, und entscheidet, ob der Plan trägt.
|
||
|
||
⚖️ **Zur Platzierung — die Messung widerspricht dem Aufstellen an einer bisherigen Stelle.**
|
||
Die Referenzmessung vom 08-22 sagt: **17 von 21 Raumabschnitten haben 0 % Verlust.** Zusätzliche
|
||
*Abdeckung* ist damit wertlos. Der ganze Wert liegt im **Gegen-AP**: Schlafzimmer −84,6 / 38 %,
|
||
Bad −82,3 / 24 %, Küche −82,0 / 23 %.
|
||
|
||
| Variante | was sie bringt |
|
||
|---|---|
|
||
| Neuer AP an **bisheriger** Stelle | besseres Deckenbild an einem Punkt, **der schon funktioniert**. Die Drei-Zimmer-Lücke bleibt **unverändert** — gleicher Standort, gleicher Schatten. |
|
||
| Neuer AP an **neuer** Stelle (Richtung Schlafzimmer/Bad/Küche) | schließt die Lücke, also genau die Redundanz, die gekauft werden soll. |
|
||
| 🎯 **Beides: cAP ax an die bisherige Stelle, der verdrängte hAP ax² wandert in den Schatten** | bestes Deckenbild dort, wo die Clients sind, **und** eine zweite Option in den drei Räumen. Braucht keine vierte Antenne, nur eine Steckdose/PoE im Zielraum. |
|
||
|
||
⇒ **Empfehlung: Variante 3.** Sie ist der einzige Weg, bei dem der Deckenstrahler an den guten Platz
|
||
darf, *ohne* dass die gemessene Schwäche unangetastet bleibt.
|
||
- **Voraussetzung Neuer AP:** PoE-in am Zielort (die cAP-ax-Reihe hat einen Gigabit-PoE-Port, kein
|
||
Netzteil im Lieferumfang üblich) und ein Trunk mit VLAN 10/30/40/50/90/99 wie bei ap1/ap2. Der AP
|
||
läuft auf `wifi-qcom` und tritt damit als **regulärer CAP** dem Manager bei — kein neues Modell im GC,
|
||
nur ein weiteres Gerät plus zwei `channel`-Objekte. ⚠️ **Exaktes Modell vor dem Kauf prüfen**
|
||
(`cAP ax` vs. `cAP XL ax`) — beide sind ax/wifi-qcom, unterscheiden sich in Abdeckung und Preis.
|
||
- **Wenn später doch der Outdoor-AP dazukommt:** er gehört nach **Band C** (30 dBm, outdoor zulässig);
|
||
Band A/B sind dann von den drei Indoor-APs belegt und die Ausweichspur `5220/Ce` ist aufgebraucht.
|
||
|
||
#### 🔴 Fund E — **WIEDER GEÖFFNET 2026-08-30: die Wiedervorlage-Bedingung ist eingetreten**
|
||
|
||
Fund E war „abgestuft, NICHT umsetzen" mit einer ausdrücklichen Bedingung: *„Erst wieder
|
||
aufgreifen, wenn ein Sticky-Fall erneut gemessen ist."* Am **2026-08-30** ist genau das passiert.
|
||
|
||
**Auslöser war eine unabhängige User-Meldung**, nicht die Suche danach: *„die letzten Tage/Wochen
|
||
regelmäßig mit dem iPhone den WiFi-Link verloren obwohl bester Kontakt. Ich lande auf 5G und nicht
|
||
am alten AP oder dem anderen."* 🔑 **Auf Mobilfunk zu fallen ist kein Roaming-Problem** — bei
|
||
schlechtem Roaming bliebe das Telefon im WLAN und wäre nur langsam. Es heißt: die Assoziation ist
|
||
gestorben und die Neuanmeldung gescheitert.
|
||
|
||
**Neue Messung (Manager-Log, 2026-08-30):** dasselbe iPhone `F2:62:4C:E1:AF:09` machte **16
|
||
Bandwechsel in 20 Minuten** zwischen `cap-wifi5` (ap3, 5 GHz) und `cap-wifi6` (ap3, 2,4 GHz),
|
||
zweimal im Abstand von **einer Sekunde**, bei **−53 … −81 dBm**. Kurz darauf dasselbe zwischen
|
||
`cap-wifi1`↔`cap-wifi2` — also **an ap2**. ⇒ **Nicht ap3-spezifisch, sondern an jedem AP.**
|
||
Das deckt sich mit der Ursprungsmessung vom 08-20 (sechs Flaps in 22 Minuten) und mit
|
||
`…@wifi2 disconnected, not responding, signal strength −47` — **Abbruch bei ausgezeichnetem Signal**.
|
||
|
||
⚠️ **Zwei Symptome, die NICHT gleichgesetzt werden dürfen:** die Band-Oszillation (beide Male
|
||
gemessen) und der Abbruch *„not responding"* bei gutem Signal. Christophs Beschreibung passt auf das
|
||
**zweite**. Ob dieselbe Ursache dahintersteht, ist **offen**.
|
||
|
||
**Zeitachse:** am **2026-08-17** liefen TP28 (CAPsMAN-Cutover) und TP27 (7.24) gleichzeitig. Vorher
|
||
waren ap1/ap2 Standalone **ohne Steering und ohne Neighbor-Group**, und `sec-main` fuhr
|
||
`ft=yes ft-over-ds=`**`no`**. Beides änderte sich an diesem Tag. Das Beschwerdefenster deckt sich —
|
||
**Korrelation, kein Beweis.**
|
||
|
||
**Was die MikroTik-Doku dazu hergibt (recherchiert 2026-08-30):**
|
||
- `transition-threshold` Default **−80 dBm**, `-time` **10 s**, `-request-period` **30 s**,
|
||
`-count` **3**. Unsere Steering-Objekte setzen **nichts** davon. Bei −53…−74 dBm wird das iPhone
|
||
also kaum Transition-Kandidat ⇒ **unsolicited BTM scheidet als Treiber weitgehend aus.**
|
||
- **WPA3 + 802.11r:** die Doku nennt **nur** „802.11r geht nicht über **OWE**". Eine
|
||
WPA3-PSK/FT-Inkompatibilität steht dort **nicht** — sie kursiert in Foren. **Hypothese.**
|
||
- **`2g-probe-delay`**: dokumentiertes Band-Steering (2,4-GHz-AP ignoriert die ersten 3
|
||
Probe-Requests je Client pro 60 s). Bei uns **nicht gesetzt**.
|
||
- Bei **dynamischen** Neighbor-Groups markiert RouterOS 5 GHz als *preferable*. Wir nutzen die
|
||
**statische** Gruppe `bf-main` über beide Bänder; ob die Präferenz dort greift, sagt die Doku
|
||
nicht ⇒ **offene Frage**.
|
||
|
||
**✅ Erledigt 2026-08-30: die Beweislage ist jetzt dauerhaft.** `wireless`- und `caps`-Logs gehen in
|
||
den Kollektor (`10.40.20.18:514`, dieselbe Senke wie TP5), und `/system logging` steht seit heute
|
||
unter der **27. ComplianceRule `system-logging`** — vorher war der Block unter keiner Regel und im
|
||
Template gar nicht vorhanden. Vorher lief alles nur in einen 75-Zeilen-Ringpuffer.
|
||
|
||
**🔴 NACHTRAG 2026-08-31 — die Daten sind da, und sie kippen die Reihenfolge.** Nach einer Nacht
|
||
Logging: **559 `wireless`-Zeilen, 439 davon (79 %) vom iPhone.** Entscheidend ist die Trennschärfe —
|
||
`reauthenticating` tritt **93 von 93 Mal bei genau einem Client** auf (dem iPhone) und **93 von 93
|
||
Mal auf genau einer SSID** (`dirtyvader-main`). Was `main` von den anderen fünf unterscheidet, ist
|
||
**ein einziges Attribut: `ft=yes ft-over-ds=yes`**. Dieselben APs, dieselben Radios, derselbe
|
||
Zeitraum — die SSIDs mit `ft=no` zeigen **null** Ereignisse. Das ist eine saubere Kontrollgruppe;
|
||
die WPA3+802.11r-Hypothese ist damit **durch eigene Messung gestützt**, nicht mehr Forenwissen.
|
||
Verlauf am 31.08.: 07:25 (10×) → 07:34 (21×), eskalierend, teils mehrere pro Sekunde, endend mit
|
||
der Flucht auf einen **25 dB schlechteren** AP (`-89 dBm`).
|
||
⇒ **`ft-over-ds=no` ist jetzt Hebel 1**, nicht `2g-probe-delay`: Letzteres adressiert
|
||
Band-Steering, hier liegt ein **Authentifizierungs**problem vor. Details und Messrezept im
|
||
[Handoff vom 2026-08-31](handoff-20260831-wlan-fund-e.md).
|
||
|
||
**🚫 Kein Funk-Parameter wird geändert, solange keine Daten da sind.** Vier Hebel, **einzeln** und je
|
||
als Transaktion (`gc-transaktion.py`). **Reihenfolge korrigiert am 2026-08-31** durch den Befund oben —
|
||
die alte Liste führte `2g-probe-delay` an, das aber **Band-Steering** adressiert, während hier ein
|
||
**Authentifizierungs**problem gemessen ist:
|
||
|
||
| # | Hebel | Begründung der Reihenfolge | Risiko | Stand |
|
||
|---|---|---|---|---|
|
||
| 1 | **`ft-over-ds=no`** auf `sec-main` | nimmt die Änderung vom 08-17 zurück; FT bleibt über `ft=yes` (over-the-air) erhalten. Kleinster Eingriff, der die verdächtige Achse trifft | gering | 🔬 **gezogen 2026-08-31 08:09**, Messung läuft |
|
||
| 2 | `ft=no` auf `sec-main` | falls (1) nicht reicht: FT komplett aus. Kostet Roaming-*Geschwindigkeit*, nicht Roaming | mittel | 🔜 nur wenn (1) nicht greift |
|
||
| 3 | `2g-probe-delay=yes` (2,4-GHz-Configs) | MikroTiks *dafür gebautes* Werkzeug; verzögert nur Probe-Antworten und **kann keinen Client stranden lassen**. Adressiert die **Band-Oszillation** — ein anderes Symptom | sehr gering | 🔜 |
|
||
| 4 | `signal-range`-Boden per `access-list` (Fund E im Original) | **kann Clients aussperren** — genau das war 08-22 das Gegenargument; nur mit gemessenem Wert | hoch | 🔜 |
|
||
|
||
#### 🔬 Hebel 1 — gezogen am 2026-08-31 08:09, Messung läuft bis 2026-09-01
|
||
|
||
**Was steht:** `sec-main` am Core auf **`ft=yes ft-over-ds=no`**. Abnahme **am Manager** über alle
|
||
Master-VAPs — `cap-wifi3/4/5/6` (= ap2 + ap3) melden `ft=true ft-over-ds=false`; `cap-wifi1/2` (= ap1)
|
||
sind leer, weil ap1 funkstill ist. Danach `remote-cap provision [find]`; alle drei CAPs kamen mit
|
||
`state=Ok` zurück, alle VAPs `running=yes` **am CAP gelesen**, und das iPhone hing 71 s später wieder
|
||
auf `dirtyvader-main` mit **`auth-type=ft-wpa3-psk`** und **frischer DHCP-Lease** `10.40.10.196` —
|
||
also die vorgeschriebene Abnahme (Lease, nicht Registration-Table). 30 Clients insgesamt zurück.
|
||
|
||
**Governance:** Transaktion `TP25-fund-e-ft-over-ds` (Scope `wifi-security`, Ablauf 2026-09-03),
|
||
Geräte-Backup `pre-tp25-funde-ftoverds.backup` auf dem Core, Snapshot
|
||
[`docs/transaktionen/TP25-fund-e-ft-over-ds.snapshot.rsc`](transaktionen/TP25-fund-e-ft-over-ds.snapshot.rsc).
|
||
Compliance danach **134/135**, und der Diff ist **exakt** `ft-over-ds=yes → no` auf `sec-main` —
|
||
kein Kollateral-Drift durch die Re-Provisionierung. Das GC-Template wird **beim Commit** nachgezogen,
|
||
nicht vorher: sonst wäre der laufende Versuch unsichtbar und `status` könnte ihn nicht mehr von einem
|
||
Versehen unterscheiden.
|
||
|
||
🔴 **Die `cap-wifiN`-Karte hat sich durch die Re-Provisionierung VERSCHOBEN** — wer die Logs vom
|
||
31.08. auswertet, muss den Schnitt kennen, sonst ordnet er Ereignisse dem falschen AP zu:
|
||
|
||
| | vor 08:09 (Log 07:xx) | ab 08:09 |
|
||
|---|---|---|
|
||
| ap1 (funkstill) | — | `cap-wifi1` / `cap-wifi2` |
|
||
| ap2 | `cap-wifi1` / `cap-wifi2` | `cap-wifi3` / `cap-wifi5` |
|
||
| ap3 | `cap-wifi5` / `cap-wifi6` | `cap-wifi4` / `cap-wifi6` |
|
||
|
||
⚠️ **Korrektur am Befund: `main` unterscheidet sich in ZWEI Attributen, nicht einem.** Neben
|
||
`ft=yes ft-over-ds=yes` fährt `sec-main` als einzige SSID **`authentication-types=wpa3-psk` allein**;
|
||
die fünf anderen sind `wpa2-psk,wpa3-psk`. Die Kontrollgruppe trennt also FT **und** WPA3-only nicht
|
||
voneinander. Das ändert nichts an der Hebelreihenfolge (die Foren-Hypothese nennt genau die
|
||
Kombination WPA3+FT), aber die Formulierung „genau ein Attribut" ist zu stark — **wenn Hebel 1 und 2
|
||
beide nichts bewegen, ist `wpa2-psk,wpa3-psk` auf `sec-main` der nächste Kandidat, nicht `signal-range`.**
|
||
|
||
**Messung (Erfolgskriterium): `reauthenticating` für `F2:62:4C:E1:AF:09` geht auf ~0, ohne dass die
|
||
anderen SSIDs neue Ereignisse zeigen. Die Zahl MUSS sich bewegen.**
|
||
|
||
```bash
|
||
ssh root@10.40.20.100 "pct exec 400 -- sh -c 'cat /opt/tp5-syslog/log/10.40.20.1/2026-0*-*.log |
|
||
grep reauthenticating | grep -oE \"([0-9A-F]{2}:){5}[0-9A-F]{2}\" | sort | uniq -c'"
|
||
```
|
||
|
||
Baseline **93** (alle iPhone, alle `main`); davon **79 am 31.08., alle in Stunde 07**, der letzte um
|
||
**07:34:43** — also **vor** dem Eingriff. Zwischen 08:00 und 08:15 kein einziges. Das ist noch **kein
|
||
Ergebnis**, nur der saubere Schnitt.
|
||
|
||
*(Der Defekt war nie gegenstandslos, nur unbelegt: Fund B/C wurden am 08-20 mit ap1 auf `ether15`
|
||
gemessen — also in genau der Stellung, in der ap1 heute wieder hängt. Er ist platzierungsunabhängig.)*
|
||
|
||
**✅ Erledigte Funde — Ergebnis und Merkregel (Volltext im Archiv):**
|
||
|
||
| Fund | Ergebnis | Merkregel, die bleibt |
|
||
|---|---|---|
|
||
| **A** 2,4 GHz fuhr 40 MHz statt der profilierten 20 | ✅ 08-17: Inline-`channel.*` auf `wifi1/wifi2` geleert → `2412`/`2462` @ 20 MHz | **Inline-Overrides gewinnen gegen das verlinkte Profil** — die SoT war die ganze Zeit richtig, das Gerät hat sie nicht befolgt |
|
||
| **B** ap2 sendete 5 GHz mit **9 statt 18 dBm** | ✅ 08-17: `5220/ax/Ce/I`, **18 dBm** | Der Fix ist gemessen, **der Mechanismus nicht** — beide ersten Erklärungen (ETSI-Band C, „80 MHz kostet generell") sind widerlegt. Resthypothese + diskriminierender Test (`ap2 auf 5180/Ceee`) stehen im Archiv und gehören **vor** einen Hardwarekauf |
|
||
| **C** 80 MHz kostete Randabdeckung | ✅ 08-17: `ch-5g` auf `20/40mhz`; ap1 `5180/Ce`, ap2 `5220/Ce`, überlappungsfrei | Unter ETSI gibt es im Indoor-Band **nur einen** 80-MHz-Block — zwei APs können dort nicht beide 80 MHz fahren. Genau deshalb war ap2 je auf 5745 gelandet |
|
||
| **D** `w2-iot` trug einen Channel-Override auf einem **Slave**-VAP | ✅ 08-17: Overrides auf beiden APs entfernt, `w2-iot` seither `BR` | `band` ist eine Eigenschaft des **Radios**, nicht eines Slave-VAPs. War der Mechanismus hinter „alle SSIDs da, `-iot` beharrlich nicht" |
|
||
| **Drift-Lücke** — genau diese Overrides lagen außerhalb jeder ComplianceRule | ✅ 08-18 mit dem TP28-GC-Umbau: `wifi-*`-Features hängen am Core, drei neue Rules; ein CAP hat keine Inline-Overrides mehr | **„Compliant" heißt nur: die abgedeckten Sektionen sind sauber.** Fund A und D blieben seit 08-08 unentdeckt, während die Flotte durchgehend compliant meldete |
|
||
|
||
🔴 **Deploy-Gotchas aus derselben Arbeit (kosten sonst zwei Deploys, Volltext im Archiv):**
|
||
`/interface/wifi/set <if> !channel` **löscht die Inline-Sub-Properties NICHT** — es wirft nicht, es tut
|
||
nichts; richtig ist einzeln `!channel.width !channel.band !channel.skip-dfs-channels`. Und bei
|
||
Kanalarbeit **Breite VOR Frequenz** ändern, sonst erzeugt man auf dem Weg eine Co-Channel-Kollision.
|
||
⚠️ `steering=`/Channel-Änderungen **re-applien alle VAPs und werfen jeden Client kurz raus** — nicht
|
||
während eines Calls deployen, `/system backup` je AP vorher.
|
||
|
||
🔬 **Messwerkzeug — vier Regeln, jede aus einem Fehlbefund geboren:**
|
||
- 🔴 **Für einen Kanal-/Umfeldscan NICHT `list_wifi_interfaces` benutzen** (Fund 2026-08-24): das Tool
|
||
gibt `security.passphrase` **aller** Profile im Klartext aus — ein Read für die Kanalbreite hat so
|
||
fünf WLAN-Passphrasen in ein Transkript geschrieben, den IoT-PSK inklusive. Für Kanäle reicht
|
||
`/interface wifi channel print detail`; für die `cap-wifiN`-Zuordnung `… print where master=yes`
|
||
mit Feldauswahl. **Der Scan selbst (`/interface wifi scan`) ist unbedenklich.**
|
||
- 🔴 **Die `cap-wifiN`-Nummern sind NICHT stabil.** Der Manager vergibt sie in der Reihenfolge, in der
|
||
sich die CAPs anmelden — nach dem ap1-Rückbau sind `cap-wifi1/2` = **ap2** und `cap-wifi3/4` = **ap1**,
|
||
umgekehrt zur früher fest verdrahteten Tabelle. ⇒ **Die Zuordnung gehört zum Messlauf**, nicht zur
|
||
Auswertungszeit: [`tp25-walk-messung.sh`](tp25-walk-messung.sh) friert sie als `ap-map.txt` ein, die
|
||
Auswertung lässt die Namen sonst roh — **unbeschriftet ist ehrlicher als falsch beschriftet.**
|
||
**Läufe vor dem 2026-08-22 sind nicht rekonstruierbar**; wer ihre AP-Namen zitiert, prüft sie an der
|
||
Physik (der AP im Raum liefert dort das stärkste Signal).
|
||
- 🔴 **Ein vom Pin abgewiesener Verbindungsversuch ist in Ping-Daten nicht von einem Funkloch zu
|
||
unterscheiden** — und der Manager protokolliert ihn auf `info`-Ebene **gar nicht**. Android quittiert
|
||
die Abweisung mit „Verbindung fehlgeschlagen" und **wählt das Netz ab**; danach misst der Rest des
|
||
Rundgangs Stille. Die Klick-Konsole schlägt deshalb Alarm, sobald das gepinnte Gerät nirgends hängt,
|
||
und schreibt nach 45 s `UNERREICHBAR=apX` — denn „der Gegen-AP trägt hier nicht" **ist das Messergebnis**.
|
||
- 📐 **Eine Runde, zwei Messpunkte** (Christophs Aufbau 2026-08-22): das frei roamende iPhone zeigt, was
|
||
ein normaler Client wählt — interessant ist der **andere**. Das S22 wird per `access-list` auf den
|
||
Gegen-AP gezwungen ([`tp25-pin-s22.sh`](tp25-pin-s22.sh)). Gute Referenz + schlechte Sonde = die
|
||
Abdeckung steht, die Rückfallebene nicht. Memory `ref_gegen_ap_messung`.
|
||
- ▶️ **Start ohne Werkzeugkette: `bash docs/tp25-runde.sh [name]`** — legt den Lauf unter
|
||
`docs/messdaten/tp25-runde-<name>/` an, friert die AP-Karte ein, startet Konsole (Port 3031) und
|
||
Messung, sagt die **vom Interface gelesene** Adresse fürs Telefon an und löst beim Beenden den Pin.
|
||
Auswertung: `tp25-gehweg-auswertung.py --dir …`.
|
||
- ℹ️ **Und das alles passiert TROTZ aktivem 802.11k/v und FT** (`st-main` mit `rrm=yes`/`wnm=yes`,
|
||
`ft-over-ds=yes`, live geprüft). Die Merkregel `ref_ft_is_not_roaming` („11k/v fehlen") ist
|
||
**historisch**. Bei −48…−55 dBm kostet eine Übergabe 0–2,6 s, meist unter einer halben Sekunde —
|
||
**nicht die Übergabe ist teuer, sondern die schwache Zone.**
|
||
|
||
**User-Randbedingung (2026-08-17, wörtlich): „ich weigere mich einen anderen access point zu kaufen
|
||
solange ich nicht das gefühl habe, wir haben alles aus dem ap rausgeholt."** Das ist die
|
||
**Abnahmebedingung dieses TPs** — keine Hardware-Empfehlung, bevor die Parameter stimmen. Der Boden ist
|
||
gelegt (beide APs 18 dBm, überlappungsfrei, 20 MHz auf 2,4 GHz, ap2 auf 2,5 m); was übrig bleibt, ist
|
||
die Redundanzfrage oben.
|
||
|
||
- **Warum eigener TP:** Christoph hat am 2026-08-16/17 sechs konkrete Beobachtungen gemeldet und
|
||
ausdrücklich **einen** TP dafür verlangt; sie hängen alle an derselben Ursache-Klasse (Funkparameter,
|
||
die nicht nach Manual gesetzt sind). **TP25 übernimmt den offenen Rest von TP3.3** (Re-Walk,
|
||
Dead-Spot-Kartierung — mit der Raumkarte vom 08-22 faktisch geleistet). TP3.2/TP3.3 stehen als Historie.
|
||
- **Stand bei Sitzungsende 2026-08-22:** Pin gelöst, Automatik aus, `crs326-core` unverändert und
|
||
**transaktionsfrei** — die `access-list` steht unter keiner ComplianceRule, es gab nichts zu committen.
|
||
*(Seit 2026-08-23 gibt es die Rule `wifi-access-list`, s. TP22-Nachtrag — Fund E wäre ab jetzt
|
||
transaktionsfähig.)*
|
||
- 📋 **Übergabe:** [`handoff-tp25-session-20260821.md`](handoff-tp25-session-20260821.md) ·
|
||
Messungen: [`tp25-wlan-roaming-messung-20260820.md`](tp25-wlan-roaming-messung-20260820.md),
|
||
[`tp25-runde-20260822-2330-auswertung.md`](tp25-runde-20260822-2330-auswertung.md)
|
||
- **Abh.:** TP22 ✅ (GC-Deploy-Pipeline). Verwandt **TP3.2** (`docs/wifi-p750w-coverage-and-map.md`),
|
||
**TP29** (nutzt dieselbe Datenquelle als Sensor). **Kein Blocker für irgendetwas.**
|
||
|
||
<a id="tp28"></a>
|
||
|
||
### TP28 — WLAN über CAPsMAN auf `crs326-core` ▸ ✅ **ap3 LIVE (2026-08-30)** — wieder geöffnet und durchgezogen
|
||
|
||
> **Der CAPsMAN-Kern ist und bleibt ✅ KOMPLETT (2026-08-18)** — der Abschnitt darunter ist
|
||
> unverändert der Archivtext inkl. Post-Mortem. Wieder geöffnet wurde das TP am 2026-08-30,
|
||
> weil mit **ap3** ein dritter CAP dazukommt; das ist dieselbe Baustelle, kein neues TP
|
||
> (Christoph-Direktive: *ein archiviertes TP ist ein geschlossenes Ticket und darf wieder
|
||
> geöffnet werden*). **Der offene Teil steht am Ende des Abschnitts unter „Nachtrag ap3".**
|
||
|
||
- 🔴🔴 **POST-MORTEM 2026-08-18 (gemeldet von Christoph: „die -iot-geräte sind offline … connect möglich, aber kein dhcp"):**
|
||
Seit dem Cutover hingen **alle Slave-VAPs in gar keiner Bridge**. `/interface wifi cap slaves-datapath`
|
||
war auf beiden APs leer — und genau dieses Feld ist das Einzige, was die vom Manager erzeugten
|
||
Slave-VAPs lokal in die Bridge haengt. Der Manager schickt `datapath.bridge=bridge` zwar mit,
|
||
**der CAP uebernimmt davon nur die `vlan-id`.** Betroffen: `dirtyvader-iot`, `-media`, `-untrusted`,
|
||
`-guest`. Nicht betroffen: `dirtyvader-main` — dessen Master-Radios `wifi1`/`wifi2` sind
|
||
**statische** Bridge-Ports und liefen deshalb weiter. Genau das machte den Ausfall unsichtbar.
|
||
- **Fix (live 2026-08-18 auf ap1 + ap2, seither Soll im Template):**
|
||
`/interface wifi datapath add name=dp-caps-slaves bridge=bridge` +
|
||
`/interface wifi cap set slaves-datapath=dp-caps-slaves`. Das Objekt traegt **bewusst keine
|
||
`vlan-id`** — die liefert der Manager pro VAP. Danach erscheinen die VAPs als **dynamische
|
||
Bridge-Ports mit `pvid`=30/40/50/90**, und DHCP laeuft sofort wieder.
|
||
- 🔴 **Die Diagnose-Lehre: „Client assoziiert" beweist gar nichts.** Am Manager standen 25 IOT-
|
||
Registrations — das WLAN sah perfekt aus. Erst zwei Reads trennten die Faelle:
|
||
`torch` auf `vlan30` am Core (**leer**) und der TP5-Drop-Log (**kein einziger DHCP-Eintrag**).
|
||
Beides leer heisst: es wird nichts verworfen, es **kommt** nichts. Damit war die Firewall
|
||
— der naheliegende Verdaechtige nach der Haertung derselben Nacht — in zwei Minuten entlastet.
|
||
- ⚠️ **Die Abnahme „✅ Ohne jeden Aussetzer" weiter unten war falsch.** Sie zaehlte Registrations
|
||
(30 Clients, beide CAPs `Ok`, 7 VAPs je AP) — also genau die Metrik, die hier luegt. Keiner
|
||
dieser Werte beruehrt die Bridge. Ein Abnahmekriterium fuer WLAN-Umbauten ist ab jetzt eine
|
||
**frische DHCP-Lease pro SSID**, nicht die Registration-Table.
|
||
- **`routeros/tp28-ap-wifi-reste-abraeumen.rsc` hat den Ausfall nicht ausgeloest, aber zementiert:**
|
||
mit den geloeschten datapath-Objekten gab es lokal nichts mehr, worauf `slaves-datapath` haette
|
||
zeigen koennen. Der Ein-Zeilen-Fix war damit weg. Nachtrag steht im Skript.
|
||
- ✅ **Reboot-Abnahme an ap2 (2026-08-18), drei Fragen auf einmal beantwortet:** `slaves-datapath`
|
||
ueberlebt den Neustart (statisches Objekt, steht im `/export`), der CAP **findet seinen Manager
|
||
durch die gehaertete input-Kette wieder** (`current-caps-man-identity=crs326-core` — MNDP/CAPWAP
|
||
sind also korrekt offen, das war seit TP5 Etappe 4 nie durchgespielt), und die sieben Slave-VAPs
|
||
kommen von selbst als dynamische Bridge-Ports mit pvid 30/40/50/90 zurueck. AP nach **40 s**
|
||
wieder erreichbar. Abnahme nach neuer Regel = **frische Lease**: IOT-Client `10.40.30.8`
|
||
`last-seen=30s`, MAIN-Client `10.40.10.180` `last-seen=1m54s`.
|
||
- ⚠️ **Reboot per MikroMCP geht auf 7.24 nicht:** das `reboot`-Tool sendet `delay`, die REST-API
|
||
antwortet `unknown parameter delay`; `run_command` ist fuer `/system reboot*` per Deny-Policy
|
||
gesperrt. Weg bis das gefixt ist: `sshpass` + `ssh admin@<ap> "/system reboot"`.
|
||
- ✅ **Soll nachgezogen, Compliance wieder sauber: 92/92, 0 Drifts** (2026-08-18). Weg: Template in
|
||
`nautobot/mikrotik_routeros.j2` → `routeros/push-gc-template.py` (Secret-Guard) → GitRepository-Sync
|
||
→ Backup/Intended/Compliance. Ohne den Push meldet der Refresh die zwei neuen Zeilen als `extra`
|
||
auf beiden APs — das Jinja lebt im **separaten** Gitea-Repo `bf-golden-config`, ein Commit hier
|
||
allein erreicht Nautobot nicht.
|
||
- ⚠️ **Beim ersten Sync-Versuch: `redis.exceptions.ConnectionError: broken pipe`** auf dem
|
||
Nautobot-Stack. Ein simpler Retry ging durch; NAS-Load war unauffaellig (0,9). Der Job meldet das
|
||
als `FAILURE` — nicht mit einem inhaltlichen Fehler verwechseln.
|
||
|
||
- ✅ **Der offene GC-Rest ist erledigt (2026-08-18).** Die fuenf `wifi-*`-ComplianceFeatures haengen
|
||
jetzt am **Core**, der AP-Zweig des Templates ist auf eine einzige Zeile eingedampft
|
||
(`/interface wifi cap set discovery-interfaces=vlanXX enabled=yes`).
|
||
- 🔴 **Dabei aufgefallen: die APs trugen noch die KOMPLETTE Standalone-WLAN-Config** aus TP3 —
|
||
je 2 channel, 6 datapath, 6 security, 7 configuration, 1 steering. Der Cutover vom 08-17 hatte
|
||
sie nie abgeraeumt. Wirkungslos (der Manager liefert alles dynamisch), aber sie im Template als
|
||
Soll zu rendern hiesse, totes Zeug zur Absicht zu erklaeren — dasselbe Argument wie bei den
|
||
defconf-Firewallregeln in TP26. Abgeraeumt mit `routeros/tp28-ap-wifi-reste-abraeumen.rsc`.
|
||
- ✅ **Ohne jeden Aussetzer:** vorher/nachher 30 Clients, beide CAPs `Ok`, 7 dynamische VAPs je AP.
|
||
Die Sorge vor einem Radio-Re-Apply (TP25/TP3.3: „wirft jeden Client kurz raus") hat sich nicht
|
||
bestaetigt — die dynamischen VAPs tragen ihre Werte **inline** (`datapath.vlan-id=…`) und
|
||
referenzieren kein lokales Objekt; nur `wifi1`/`wifi2` hielten noch einen toten
|
||
`configuration=`-Verweis, der beim Loeschen folgenlos blieb.
|
||
- 🔴 **`[find !dynamic]` ist auf diesen Sektionen ein SYNTAXFEHLER**, nicht bloss wirkungslos:
|
||
`/interface wifi configuration` & Co. haben gar keine `dynamic`-Eigenschaft (die gibt es nur auf
|
||
`/interface wifi` selbst). Der erste Anlauf lief komplett in die `on-error`-Zweige, aenderte
|
||
**nichts** und meldete trotzdem „Plan Completed". Wieder: nur die Gegenpruefung am Geraet zaehlt.
|
||
- ✅ **Drei neue ComplianceRules schliessen die letzten WLAN-Luecken:** `wifi-capsman`
|
||
(der Manager-Schalter), `wifi-provisioning` (die vier Regeln je `radio-mac` — sie entscheiden,
|
||
welcher AP welche Kanaele und Slave-VAPs bekommt) und `wifi-cap` (die CAP-Seite; faellt
|
||
`discovery-interfaces` weg, findet der AP seinen Manager nach dem naechsten Reboot nicht mehr).
|
||
**Ergebnis: 92/92 compliant, 0 Drifts.**
|
||
- ⚠️ **`wifi-security` ist gruen, aber NICHT deploybar.** Die Passphrasen stehen als `<<REDACTED>>`
|
||
im Template (User-Entscheid 2026-08-18: alle fuenf Features umhaengen, Platzhalter in Kauf nehmen).
|
||
Der gruene Haken beweist nur, dass Soll und Ist dieselbe **Maske** tragen — nicht, dass die
|
||
Passphrasen stimmen, und schon gar nicht, dass die Sektion ausgerollt werden koennte. Das steht
|
||
als Warnung im Template. Wer sie aendert, liest den Ist-Stand per REST zurueck
|
||
(`GET http://10.40.99.1/rest/interface/wifi/security`) — **nicht** aus `wlan-psks.env` oder den
|
||
drei Bring-up-Dateien, die voneinander abgeschrieben sind.
|
||
- ⚠️ **Die frueher hier notierte „bisher fehlende Rule auf `/interface wifi`" ist gegenstandslos** —
|
||
wie vorhergesagt: ein CAP hat keine Inline-Overrides mehr, die driften koennten.
|
||
> **Methodenwechsel auf Christophs Anstoß:** *„ich möchte die methode in frage stellen … eine sot ist zwar nett,
|
||
> aber wenn sie uns das leben zur hölle macht, verzichte ich auf diese governance."* Der Entscheid vom
|
||
> 2026-07-22, CAPsMAN abzuschaffen, ist damit **revidiert**.
|
||
|
||
- ✅ **Warum es diesmal trägt:** 2026-07-22 war **ap1 Manager UND AP** — und ein Manager kann seine eigenen
|
||
Radios nicht per CAPsMAN provisionieren (MikroTik-bestätigt). Genau diese Asymmetrie war die Ursache von
|
||
TP3.1. Mit dem **Core** als Manager sind beide APs symmetrisch. **Der Core braucht dafür kein `wifi-qcom`:**
|
||
das `capsman`-Menü steckt im Basis-`routeros`, die 3,35 MB freier Flash sind irrelevant. Die zweite
|
||
Begründung von damals („WLAN instabil") war ohnehin widerlegt — standalone blieb es instabil, die Ursachen
|
||
waren die Funkparameter aus TP25.
|
||
- ✅ **Ist-Stand:** Manager auf `vlan99`, 4 Kanäle · 6 Datapaths · 6 Security · 2 Steering · 9 Configurations ·
|
||
4 Provisioning-Regeln je `radio-mac`. Beide `remote-cap` **`Ok`**, je 7 dynamische VAPs. Kanäle exakt wie in
|
||
TP25 gemessen (ap1 `5180/ax/Ce/I`+`2412/ax`, ap2 `5220/ax/Ce/I`+`2462/ax`). **34 Clients** (main 3, iot 27,
|
||
media 4), Roaming im Manager-Log belegt. **`ft-over-ds=yes`** ist jetzt aktiv (unter CAPsMAN erstmals
|
||
verfügbar), `-guest` hat eigenes `st-guest` mit **`neighbor-group=bf-guest`** — nicht `bf-main` mitbenutzen,
|
||
sonst meldet 802.11k Gast-Clients BSSIDs einer SSID, auf die sie nicht assoziieren können.
|
||
- 📌 **Local-Forwarding ist keine Einstellung mehr:** im neuen `/interface/wifi` gibt es **kein** zentrales
|
||
Forwarding („There is no CAPsMAN-forwarding in WiFi (RouterOS 7.0+)"). Die `datapath`-Property existiert
|
||
deshalb nicht — `monitor` bestätigt `traffic processing on CAP`.
|
||
- 🔴 **DIE MESSFALLE DIESES TPs — unter CAPsMAN führt der MANAGER die Tabellen, nicht der CAP.**
|
||
`/interface/wifi/registration-table` und die `wireless`-Logs sind auf ap1/ap2 **leer**, obwohl das WLAN
|
||
einwandfrei läuft. Ich habe daraus zweimal „WLAN ist unten" geschlossen, während Christophs Geräte mit
|
||
vollem Ausschlag verbunden waren und ein Speedtest gut lief. **Immer am Manager zählen.**
|
||
- 🔴 **DIE ZWEITE FALLE — drei übereinstimmende Dateien sind nicht drei Belege.** `wlan-psks.env` wich für
|
||
iot/untrusted/infra von `ap1-prov-base.rsc`, `ap2-prov-base.rsc` und `ap1-standalone.rsc` ab. Ich habe die
|
||
drei als Bestätigung gelesen und die env-Datei „korrigiert" — **~20 Shellys waren 20 Minuten aus dem Netz.**
|
||
Die drei Dateien stammen alle aus dem Bring-up vom 2026-08-08, sind also **eine Quelle in drei Kopien**.
|
||
✅ **Auflösung:** die Passphrasen sind am Gerät per REST **lesbar**
|
||
(`GET http://10.40.99.1/rest/interface/wifi/security`) — dort ausgelesen ergab sich exakt der env-Wert.
|
||
`wlan-psks.env` ist neu aus dem Live-Stand befüllt und trägt jetzt eine Kopf-Warnung; die drei Baselines
|
||
sind seit dem Cutover ohnehin nicht mehr deploybar und wurden entschärft (Passphrasen raus, HISTORISCH).
|
||
- ✅ **~~OFFEN — das GC-Modell beschreibt weiter Standalone-APs~~ — ERLEDIGT am 2026-08-18.** Die
|
||
`wifi-*`-Features haengen jetzt am Core, das AP-Template ist auf die `cap`-Zeile eingedampft, und die
|
||
toten Standalone-Objekte sind von beiden APs abgeraeumt. Details im ersten Block dieses TPs.
|
||
Die hier genannte Zahl `76/76` ist damit doppelt ueberholt (aktuell 92/92 bei 23 Rules).
|
||
*(Der Absatz stand bis 2026-08-19 unveraendert als „OFFEN" hier, obwohl der Kopf des TPs schon
|
||
„KOMPLETT" sagte — ein Selbstwiderspruch im selben Abschnitt, ueber den prompt die naechste Session
|
||
gestolpert ist. Merkregel fuer kuenftige Statuswechsel: **nicht nur den Header umschreiben, sondern
|
||
die Punkte darunter mitziehen.**)*
|
||
- 📌 **Nebenbefund:** HAs `mikrotik`-Integration pollt alle 10 s und schreibt zwei Log-Zeilen pro Poll auf jedes
|
||
Gerät. Die AP-Logs sind dadurch für Diagnose praktisch unbrauchbar (`get_log` ohne `topics`-Filter liefert
|
||
nur `admin logged in/out via api`).
|
||
- **Abh.:** TP25 ✅ (die Funkparameter sind übernommen), TP15/TP21.1 ✅ (Core als L3-Router = Manager-Standort).
|
||
Verwandt: **TP3** (dessen CAPsMAN-Ära hier revidiert wird), **TP22** (GC-Scope).
|
||
|
||
|
||
#### ↩️ Nachtrag ap3 — dritter Access-Point (cAP ax), offen seit 2026-08-30
|
||
|
||
**Gerät:** MikroTik **cAP ax** (`cAPGi-5HaxD2HaxD`), Werkszustand, Uplink an **`crs326-core ether21`**.
|
||
**Mgmt-IP `10.40.99.13/24`** (TP32: `.10–.29` = physische Hosts; ap1 `.11`, ap2 `.12`), statisch am
|
||
Gerät wie ap1/ap2 — **keine** DHCP-Reservierung.
|
||
|
||
**Warum überhaupt:** ap3 soll **ap1 in Technik UG ablösen**, ap1 wandert danach ans Sideboard in die
|
||
gemessene Schwäche (Schlafzimmer/Bad/Küche). Das ist exakt **TP25 Variante 3** („cAP ax an die
|
||
bisherige Stelle, der verdrängte hAP ax² wandert in den Schatten").
|
||
|
||
**Kanalplan = der aus TP25 (2026-08-24 gerechnet, DFS von Christoph genehmigt) — nicht neu erfunden:**
|
||
|
||
| AP | 5 GHz | Band | DFS | 2,4 GHz | wann |
|
||
|---|---|---|---|---|---|
|
||
| **ap3** | `5180/Ce` (36+40) | A | nein | **2437 (ch6)** | **jetzt** |
|
||
| ap2 | `5220/Ce` heute → **`5300/Ce`** (60+64) | A → B | → ja | 2462 (ch11) | beim ap1-Umzug |
|
||
| ap1 | — (**Radios aus**) → **`5260/Ce`** (52+56) | → B | → ja | 2412 (ch1) | beim ap1-Umzug |
|
||
| — | `5220/Ce` **Ausweichspur frei halten** | A | — | — | — |
|
||
|
||
🔴 **ap1s Radios gehen AUS, bevor ap3s angehen** (User-Vorgabe 2026-08-30): beide stehen bis zum
|
||
Umzug **nebeneinander**, ein zweiter AP am selben Punkt bringt nichts und stört. Das ist ein
|
||
**Interim**, kein Endzustand — ap1 kommt auf `5260` zurück.
|
||
|
||
> 📌 **Erratum 2026-08-30:** der Port ist **`ether21`**, nicht `ether22` (erste Annahme aus der
|
||
> Fragerunde). Der Befund lag die ganze Zeit in den Daten: in der dynamischen `pvid`-VLAN1-Zeile
|
||
> steht `current-untagged=…,ether21` — `ether22` taucht dort nicht auf, weil es keinen Link hat.
|
||
> **Merkregel: bevor man einen Port konfiguriert, den behaupteten Port gegen `link-ok` + die
|
||
> Bridge-Host-Tabelle stellen.** `/interface bridge host print where interface=etherN` nennt die
|
||
> MAC des Gegenübers — hier `D0:EA:11:FF:16:9D`, exakt die `E01` vom ap3-Label.
|
||
|
||
##### Stand 2026-08-30 — ✅ DURCH
|
||
- ✅ **`core ether21` ist Trunk** (10/20/30/40/50/90/99 tagged, Muster `ether15`), GC-Template
|
||
nachgezogen; AP-Zweig rendert Bridge-Ports aus **`ap.extra_ports`** statt hartkodiert.
|
||
- ✅ **Zugang über `ap3 ether2` → `core ether22`** (Patchkabel). Über `ether1` war ap3
|
||
grundsätzlich nicht administrierbar, s. Gotchas unten. Loop-Schutz: `ether21` war während
|
||
Phase 1 abgeschaltet, danach im selben Befehl getauscht (`ether22` aus, `ether21` an).
|
||
- ✅ **Bring-up in zwei Phasen**: Phase 1 (`ether1` aus der WAN-Liste, `10.40.99.13/24` auf
|
||
`vlan99`, Flotten-PW) über das Kabel, Phase 2 (Werks-WLAN weg, CAP-Modus,
|
||
`slaves-datapath`, `vlan-filtering=yes`) über `vlan99`.
|
||
- ✅ **RouterOS 7.20.8 → 7.24.1 + RouterBOOT 7.24.1** (zwei Reboots, TP27-Kriterium
|
||
`current-firmware = upgrade-firmware`). 🆕 **Die Angleichung der übrigen vier ist am
|
||
2026-08-31 erfolgt** (Christoph, WebFig) — die Flotte ist einheitlich, TP27 erledigt.
|
||
- ✅ **ap1 funkstill** (Provisioning-Regeln entfernt + `remote-cap provision`), gegengeprüft
|
||
**am CAP**: nur noch `wifi1`/`wifi2` mit `running=false`, keine dynamischen VAPs.
|
||
- ✅ **ap3 funkt**: `cap-wifi5` = **`5180/ax/Ce/I`**, `cap-wifi6` = **`2437/ax`**, voller
|
||
VAP-Satz (2 Master + 7 Slaves), alle `running=true`.
|
||
- ✅ **Datapath bewiesen**: 20 IOT-Clients auf ap3, Ping über VLAN30 mit **0 % Loss**.
|
||
*(Bewusst nicht über die Registration-Table abgenommen — die zeigte im 14-h-Ausfall
|
||
25 zufriedene Clients. Bei statisch reservierten Shellys erzeugt ein Roam keine neue
|
||
DHCP-Lease, deshalb ist hier Datenverkehr das belastbare Signal.)*
|
||
- ✅ **Nautobot**: DeviceType `cAP ax`, Device `ap3` mit 16 Interfaces, `primary_ip4`,
|
||
`secrets_group`, ConfigContext (`5g=ch36 2g=ch6 extra_ports=[]`).
|
||
Nebenkorrektur: ap1 stand auf „Wohnzimmer Sideboard", steht aber in **Technik UG**.
|
||
- ✅ **Compliance 130/130, 0 Drifts über 5 Geräte.** Die drei Werksreste, die der erste
|
||
Lauf mit ap3 gemeldet hat (defconf-DNS, 11 defconf-Firewallregeln, fehlende
|
||
`/ip service`-Härtung), sind mit `routeros/tp28-ap3-werksreste.rsc` abgeräumt.
|
||
- 🔜 **Offen:** TP25-Messrunde (`bash docs/tp25-runde.sh ap3-bringup`) — braucht einen
|
||
Rundgang. Leitfrage: deckt ap3 allein ab, was ap1 abgedeckt hat?
|
||
- 🔜 **Offen:** Passwort-Rotation (TP30) — das gemeinsame Admin-PW ist in dieser Session
|
||
verbrannt; ap3 trägt es bereits mit, hängt also in der Rotation mit drin.
|
||
|
||
##### Was am cAP ax anders ist als am hAP ax² — und was daran wehtut
|
||
- 🔴 **Nur ZWEI Ethernet-Ports** (`ether1` PoE-in, `ether2` PoE-out) — kein `ether3/4/5`. Der
|
||
GC-Template-AP-Zweig hatte die **hartkodiert**; das war die stille Annahme *„AP == hAP ax²"*.
|
||
Deshalb `ap.extra_ports` im ConfigContext (Default = `["ether3","ether4","ether5"]`, ap3 = `[]`).
|
||
**Merkregel: eine Literal-Liste im Template ist so lange harmlos, bis die Flotte heterogen wird.**
|
||
- **`crs326-core` hat kein PoE-out** (`/interface ethernet poe` existiert dort nicht). Der cAP ax
|
||
bringt aber **Netzteil *und* Gigabit-PoE-Injector** mit (Produktseite 2026-08-30) — die TP25-Notiz
|
||
„kein Netzteil im Lieferumfang üblich" trifft auf dieses Modell **nicht** zu.
|
||
- `ether2` bleibt im Bring-up als **Lifeline** im Bridge (untagged VLAN1) und fliegt erst raus,
|
||
wenn ap3 über `10.40.99.13` steht — Zielzustand = wie ap2: `ether2` **kein** Bridge-Port
|
||
(VLAN1 bleibt leer, TP19 ist geschlossen).
|
||
- **Radio-MACs nicht raten:** `/interface wifi radio print` **am Manager** listet CAP-Identity +
|
||
Radio-MAC ohne jede Passphrase. Das ist der richtige Read — **nicht** `list_wifi_interfaces`,
|
||
das gibt `security.passphrase` *aller* Profile im Klartext zurück (Leak 2026-08-24).
|
||
|
||
##### Auflagen aus TP25 für den ap1-Umzug (nicht vergessen)
|
||
1. **DFS-Channel-Objekte brauchen eine Frequenz-LISTE** (z.B. `frequency=5260,5280,5220`). Mit
|
||
genau einem Wert ist eine Radar-Erkennung ein **SPOF**: das Radio muss den Kanal verlassen und
|
||
hat keinen zweiten → 5 GHz dieses APs bleibt unten.
|
||
2. **Vorher die *erreichte* `tx-power` messen** (`/interface wifi monitor`), ap2 testweise auf
|
||
`5260` gegen die heutigen 18 dBm auf `5220`. Fund B war genau das Muster: 18 dBm gesetzt,
|
||
9 dBm gesendet.
|
||
3. Uplink für ap1 danach: **`core ether3`** (`no-link`, frei) — vorher dort die Altlast abräumen,
|
||
der Port ist untagged-Member von VLAN40 bei `pvid=1`.
|
||
|
||
|
||
### TP29 — Anwesenheitserkennung fürs Energiesparen ▸ 💡 **IDEE, noch nicht auf dem kritischen Pfad** (angelegt 2026-08-21)
|
||
> **User-Formulierung (2026-08-21):** „Personen im Haus orten mit BT (Shellys), WiFi und Radar. So
|
||
> kommen wir dem Ziel der Energieersparnis ohne Komfortverlust groß näher."
|
||
|
||
- **Ziel:** raumgenaue Anwesenheit als Eingangssignal für Heizung und Licht — sparen, **ohne** den
|
||
typischen Komfortverlust („erst kalt, dann warm, wenn jemand da ist").
|
||
- 🔎 **Der Entwurfsgedanke sind die komplementären Schwächen der drei Quellen** — keine trägt allein:
|
||
| Quelle | erkennt | erkennt **nicht** |
|
||
|---|---|---|
|
||
| **BLE über die Shellys** | grobe Zonen, billig, überall vorhanden | Menschen ohne getragenes Gerät; Raumgrenzen unscharf |
|
||
| **WLAN** (Registration-Table am Manager: Gerät, AP, Signal) | wer mit Telefon da ist, inkl. AP-Zuordnung | Menschen ohne Telefon; Raum nur so genau wie die AP-Abdeckung |
|
||
| **mmWave-Radar** | auch die **stillsitzende Person ohne Gerät** | braucht einen Sensor **je Raum** |
|
||
- ✅ **Was schon steht:** Shellys vollständig auf VLAN30 (TP17 ✅), HA produktiv — und mit
|
||
[`docs/tp6-monitor-core.sh`](tp6-monitor-core.sh) existiert seit TP25 bereits ein Sammler, der die
|
||
WLAN-Präsenzdaten am Manager mitschreibt (Gerät → AP → Signal, im Sekundentakt).
|
||
- ❓ **Offene Fragen für die erste Session:** Datenpfad ins HA (MQTT oder REST?) · Firewall-Regeln für
|
||
neue Sensorik (**TP5 ist deny-by-default**, jeder neue Dienst über eine VLAN-Grenze braucht eine
|
||
Regel) · Aufbewahrung und Privatsphäre der Ortsdaten · ob Radar-Sensoren auf `dirtyvader-iot` laufen
|
||
oder eigene Segmentierung brauchen.
|
||
- **Abgrenzung:** TP25 **misst** den Funk, TP29 **nutzt** ihn als Sensor — dieselbe Datenquelle,
|
||
anderes Ziel. Die Raumkarten aus TP25 sind für TP29 direkt verwertbar: sie sagen, wie genau die
|
||
WLAN-Quelle einen Raum überhaupt auflösen kann.
|
||
- **Abh.:** keine harten. Profitiert von TP25 (Abdeckung/AP-Zuordnung) und TP11 (Monitoring), wenn es
|
||
dahin kommt. **Kein Blocker für irgendetwas.**
|
||
|
||
<a id="tp30"></a>
|
||
### TP30 — Credentials: neu setzen & rotieren (Sammelbecken) ▸ 🔜 **ANGELEGT 2026-08-24, bewusst GEBÜNDELT gegen Projektende**
|
||
> **User-Entscheid 2026-08-24:** „ich würde gerne daraus einen eigenen punkt machen. ich bin mir
|
||
> sicher, dass es noch öfters passwörter zu rotieren geben wird. das machen wir dann alles gegen
|
||
> projektende." — nachgeschärft am selben Tag: **„tp30 wird unser sammelbecken für credentials,
|
||
> die neu gesetzt werden müssen"**, und **TP17.2 wandert hierher.**
|
||
>
|
||
> 📥 **Scope ist damit breiter als „Rotation": jedes Credential, das neu gesetzt werden muss** —
|
||
> **Erstvergabe** (bisher gar keins vorhanden, z.B. Shelly `SetAuth`) **oder Rotation** (Wert
|
||
> verbrannt/abgelaufen). Die Trennung ist für die Priorität wichtig, für die Arbeit nicht: der
|
||
> Kostentreiber ist in beiden Fällen derselbe — die Konsumenten, nicht das Setzen.
|
||
> **Neue Credential-Aufgaben kommen ab jetzt hier hinein, nicht als eigenes TP.**
|
||
>
|
||
> **Warum ein eigener TP und nicht vier verstreute Follow-up-Zeilen:** die Rotationen teilen sich Werkzeug,
|
||
> Fallen und vor allem den **teuren Teil — den Konsumenten** (HA, Nautobot, MikroMCP, `~/.secrets`).
|
||
> Wer sie einzeln macht, zahlt den Re-Auth-Aufwand mehrfach. Gebündelt ist es **ein** Durchgang je
|
||
> Gerät. Deshalb steht hier die Liste, nicht der Termin.
|
||
|
||
- **Auslösekriterium (statt Datum):** wenn die offenen TPs abgearbeitet sind bzw. Christoph das
|
||
Projekt schließt. 🔴 **Harte Ausnahme, die den Termin vorzieht:** ein *neuer* Leak, ein
|
||
fremdzugängliches Gerät oder ein Personenwechsel — dann sofort und einzeln, nicht gebündelt.
|
||
|
||
| # | Credential | Umfang | Anlass | Vorarbeit | Preis der Rotation |
|
||
|---|---|---|---|---|---|
|
||
| 1 | **MikroTik `admin`** | core · branch · ap1 · ap2 (**ein** geteilter Wert) | 🔴 **zweimal in ein Transkript geraten** (2026-08-22 via `sshpass -p`; 2026-08-23 durch einen Enumerationsfehler) | ✅ **Etappe 1 durch** (s.u.) | nach Etappe 2 nur noch `~/.secrets/credentials.env` + `~/.claude.json` + MikroMCP-Neustart |
|
||
| 2 | **IoT-WLAN-PSK** (`sec-iot`) | `dirtyvader-iot`, 26 Clients | steht **im Klartext** im TP17-Text ⇒ auch in der **Git-Historie**; Redigieren holt ihn dort nicht raus | Hebel `multi-passphrase` am Core verifiziert vorhanden, Tabelle leer | 23 Shellys per Skript + **3 von Hand** (`.249` NSPanel Pro, `.244` Nuki Bridge, `.227` SwitchBot) |
|
||
| 3 | **Shelly-Gerätepasswort** *(vormals TP17.2)* | 23 Shellys | Härtung (bisher **keine** Auth auf den Geräten) | ✅ Werkzeug fertig (`migrate.py cmd_auth` + Digest-Fallback) | **je Gerät ein HA-Re-Auth-Flow** — das ist der Aufwand, nicht `SetAuth` |
|
||
| 4 | **CF-Access-Service-Token** (`CF_API_TOKEN`) | Cloudflare-Zugang, s. TP14.4 | ⏳ **läuft am 2027-01-09 ohnehin ab** — harter Termin, unabhängig vom Projektende | Runbook liegt (`tp14.4-cf-access-token-runbook.md`) | Token neu ausstellen + `~/.secrets` nachziehen; **account-owned**, anderer verify-Endpoint |
|
||
|
||
> ✅ **Der Leak-MECHANISMUS hinter #1 ist seit 2026-08-31 zu — der Wert bleibt trotzdem verbrannt.**
|
||
> Alle sechs Skripte im Repo, die `sshpass -p <PW>` benutzten (`nautobot/gc-transaktion.py` und
|
||
> `routeros/import-core-rsc.py` sowie die vier TP15-Cutover-Skripte, die am selben Tag nach
|
||
> `routeros/archiv-tp15-cutover/` gewandert sind), laufen jetzt über `SSHPASS`-Env + `sshpass -e`
|
||
> — **auch die archivierten**, denn Archiv-Code wird abgeschrieben. **Kontrolliert gemessen**, nicht behauptet: mit `-e` taucht
|
||
> das Passwort in 40 `ps`-Stichproben während einer laufenden Verbindung **nicht** auf, und die
|
||
> Negativkontrolle (ein Dummy-Wert über die alte `-p`-Form) wurde **36-mal** gesehen — die Probe ist
|
||
> also nicht blind. 🔴 **Das ändert an #1 nichts:** ein Wert, der einmal in einem Transkript stand,
|
||
> wird nicht dadurch sauber, dass der Weg dorthin geschlossen ist. Rotation bleibt fällig.
|
||
>
|
||
> ℹ️ **#3 war bis 2026-08-24 TP17.2** und ist jetzt vollständig hier (s. Detailabschnitt unten) —
|
||
> es benutzt denselben Geräte-Durchgang wie #2, **ein Reboot statt zwei**. #4 gehört sachlich zu
|
||
> **TP14.4**, das als TP bestehen bleibt (dort steckt das CF-Access-Profil, nicht nur der Token).
|
||
|
||
#### Posten #1 im Detail — MikroTik `admin`: Radius, Etappe 1, Etappe 2
|
||
*Am 2026-08-24 aus den Follow-ups hierher gezogen — Text unverändert übernommen.*
|
||
|
||
**Vorgeschichte des Entscheids** (Stand bis 2026-08-23, User: „machen wir, aber erst später"):
|
||
⏭️ ~~**Nachgelagert seit 2026-08-23: erst TP17.1, dann rotieren.**~~
|
||
**Revidiert noch am selben Tag (User): beides läuft PARALLEL.** Der Passphrase-Override je MAC
|
||
(s.u.) macht den Flag-Day überflüssig, damit war die Reihenfolge nie technisch nötig — nur
|
||
ökonomisch. Regel für die Parallelität: **ein Gerät, das ohnehin auf Thread/Zigbee wandert,
|
||
bekommt den neuen PSK gar nicht**, sondern nur einen Übergangs-Eintrag in der `access-list`.
|
||
⚠️ Der Entscheid betrifft **beide** Passwörter: der **IoT-WLAN-PSK** steht im Klartext im
|
||
TP17-Text ([Archiv](teilprojekte-archiv.md#tp17)) und damit auch in der Git-Historie — **bleibt
|
||
auf User-Entscheid 2026-08-23 vorerst unverändert**, weil Redigieren ihn nicht aus der Historie
|
||
holt und Rotieren heute ~22 Shellys kostet. ~~Nach TP17.1 sind es deutlich weniger.~~
|
||
🔴 **Hinfällig seit 2026-08-23: TP17.1 ist verworfen** — die Geräte bleiben WLAN-Clients, es
|
||
werden also *nicht* weniger. Die Rechnung „~23 Shellys + 3 Fremdgeräte" gilt unverändert. Anlass: das Core-Passwort ist bei einer Debug-Ausgabe im Klartext in
|
||
ein Sitzungstranskript geraten. Ursache war `sshpass -p`, das es in die **Kommandozeile**
|
||
schreibt — sichtbar in `ps` und in jedem argv-Trace.
|
||
#### 🔑 Radius-Befund 2026-08-23: das Admin-Konto teilen sich FÜNF Konsumenten
|
||
Vor dem Rotieren erhoben — das ist deutlich mehr als „vier Geräte + eine Datei":
|
||
|
||
| Halter | Nachziehen |
|
||
|---|---|
|
||
| `~/.secrets/credentials.env` | Datei |
|
||
| `~/.claude.json` (MikroMCP-Env, `ROUTER_*`/`CRS326_*`) | JSON + MCP-Neustart |
|
||
| **Nautobot-Stack** `NAUTOBOT_ROUTEROS_USERNAME/PASSWORD` | Stack-Datei + **Portainer-Redeploy auf der DS923** |
|
||
| **HA: VIER `mikrotik`-Integrationen** (`.99.1/.2/.11/.12`, 40 `device_tracker`) | 🔴 **`supports_reconfigure: false` — NICHT skriptbar**, 4× Re-Auth von Hand |
|
||
| Repo-Skripte (`routeros/*.py`, `nautobot/*.py`, `docs/tp*.sh`) | ziehen aus `~/.secrets` → automatisch |
|
||
|
||
⇒ **User-Entscheid 2026-08-23: Konten trennen statt ein Passwort teilen.** Nicht das Rotieren ist
|
||
das Problem, sondern dass ein einziges `admin` von fünf Stellen benutzt wird — jede Rotation kostet
|
||
sonst wieder vier Re-Auths und einen Stack-Redeploy.
|
||
|
||
#### ✅ Etappe 1 DURCH (2026-08-23): dedizierte Service-Accounts auf allen vier Geräten
|
||
Rein **additiv**, kein laufender Dienst berührt. Vorher existierte **nur** `admin` (Gruppe `full`).
|
||
|
||
| Gruppe | Policy | User | Zweck |
|
||
|---|---|---|---|
|
||
| `grp-ha` | `read,api,rest-api,test` | `ha` | HA-Mikrotik-Integration, **lesend** über API 8728 |
|
||
| `grp-nautobot` | `ssh,read,write,test,sensitive` | `nautobot` | Golden Config über SSH/Netmiko (Backup **und** Deploy) |
|
||
|
||
Passwörter: `MIKROTIK_HA_PASS` / `MIKROTIK_NAUTOBOT_PASS` in `~/.secrets/credentials.env`
|
||
(32 Zeichen, je Dienst eigen, geräteübergreifend gleich).
|
||
🔒 **Angelegt über ein Skript, das die Werte aus der Datei liest** — bewusst *nicht* über
|
||
`manage_user`, dessen `password`-Parameter im Sitzungstranskript landen würde.
|
||
|
||
**Abnahme, auf allen vier Geräten geprüft:**
|
||
- `ha` liest (`identity`, `dhcp-lease` — am Core 59 Einträge, genau was das Device-Tracking braucht),
|
||
**Schreibversuch wird abgewiesen**.
|
||
- `nautobot` meldet sich per SSH an und exportiert; **kein REST** (bewusst kein `rest-api` in der Policy).
|
||
- 🎯 **Der Export von `nautobot` ist byte-identisch zu dem von `admin`** (391/56/87/87 Zeilen je Gerät)
|
||
⇒ **die GC-Compliance driftet beim Umstellen nicht.** Das war das Hauptrisiko dieser Etappe.
|
||
- `/user` wird von **keiner** ComplianceRule abgedeckt ⇒ die neuen Konten erzeugen selbst keinen Drift.
|
||
|
||
#### ⏭️ Etappe 2 offen — die Konsumenten umhängen, DANN `admin` rotieren
|
||
1. **Nautobot** auf `nautobot` umstellen (Stack-Env + Portainer-Redeploy), GC-Backup-Lauf als Abnahme.
|
||
2. **HA**: die vier `mikrotik`-Einträge auf den `ha`-User bringen. ⚠️ Reconfigure ist nicht skriptbar;
|
||
Entfernen-und-neu-Anlegen **wäre** skriptbar, riskiert aber die 40 `device_tracker`-Entity-IDs
|
||
(sie leiten sich aus der MAC ab, kämen also gleich zurück — **aber erst nachdem** die alten
|
||
Registry-Einträge weg sind, sonst hängt HA ein `_2` an; dieselbe Falle wie beim Shelly-Rename).
|
||
3. **Erst danach** `admin` rotieren — dann berührt es nur noch `~/.secrets` + `~/.claude.json`.
|
||
|
||
🔴 **Eigener Fehler, protokolliert:** beim Erheben des Radius hat ein `json.dumps`-Aufruf über
|
||
`~/.claude.json` das **ap1-Passwort im Klartext ins Transkript geschrieben**. Da alle vier Geräte
|
||
dasselbe `admin`-Passwort teilen, ist es **derselbe Wert, der am 2026-08-22 bereits verbrannt ist** —
|
||
keine neue Expositionsklasse, aber ein zweiter Beleg dafür, dass die Rotation fällig ist.
|
||
**Merkregel: Secrets-Enumeration gibt nur Schlüsselnamen aus, nie Werte** — auch nicht abgeschnitten.
|
||
|
||
✅ **Vorbedingung erfüllt, geprüft 2026-08-23:** alle vier
|
||
Skripte laufen auf `sshpass -e` (`tp6-marker/server.py`, `tp6-monitor-core.sh`,
|
||
`tp25-walk-messung.sh`, `tp25-pin-s22.sh`) — die frühere Notiz, zwei davon hätten die Schwäche
|
||
noch, war veraltet. Übrig blieb nur **`CLAUDE.md`, das `sshpass -p` weiterhin aktiv als Reboot-Weg
|
||
empfahl** — am 2026-08-23 auf `sshpass -e` korrigiert. **Damit ist die Rotation ohne Rückfallgefahr
|
||
fahrbar.**
|
||
Weg: `manage_user set-password` (⚠️ `/user set*` ist per Deny-Policy blockiert), danach
|
||
`~/.secrets/credentials.env` (`ROUTER_*_PASS`/`CRS326_*_PASS`) und `~/.claude.json` nachziehen.
|
||
Es ist **ein** Passwort für alle vier Geräte — die „Überlebensversicherung" aus CLAUDE.md.
|
||
- 🔎 **Nachtrag 2026-08-23 (Recherche-Session, read-only — kein Eingriff): der PSK in der Doku ist
|
||
noch der LIVE konfigurierte.** Gegengeprüft am Manager `crs326-core` (`/interface wifi security`,
|
||
Profil `sec-iot` → `cfg-iot` → SSID `dirtyvader-iot`, `datapath.vlan-id=30`,
|
||
`wpa2-psk,wpa3-psk`) — und zwar **per Fingerprint statt Klartext** (Länge + drei Zeichenpaare),
|
||
damit der Wert nicht ein zweites Mal in ein Transkript wandert. **Diese Methode ist die Merkregel
|
||
daraus:** einen Verdacht auf Übereinstimmung prüft man mit Stichproben, nicht durch Ausgeben.
|
||
- ✅ **User-Entscheid 2026-08-23 zur Git-Historie: belassen, im Nachtrag dokumentieren** — **kein**
|
||
`git filter-repo`. Begründung trägt: genau **eine** getrackte Fundstelle
|
||
([Archiv](teilprojekte-archiv.md#tp17)), drei Commits (`e76289c` Ersteintrag 2026-07-11,
|
||
`f0d1ff9` 08-08, `7ee64b5` Doku-Split 08-23), Remote **ausschließlich** `gitea.emsenhuber.net`,
|
||
**ein** lokaler Klon. Eine History-Rewrite hätte hier also nichts gerettet, was nicht ohnehin nur
|
||
auf der eigenen Infrastruktur liegt — und den Preis (alle Hashes neu) für nichts bezahlt.
|
||
- 📊 **Warum die Rotation heute teuer ist — live gemessen 2026-08-23:** **26 der 27 WLAN-Clients des
|
||
gesamten Netzes hängen auf `dirtyvader-iot`** (`dirtyvader-main` 1, MEDIA/UNTRUSTED/GUEST je 0).
|
||
VLAN30 führt **41 Leases/Reservierungen** (5 aktuell `bound`, der Rest sind statische
|
||
Reservierungen ausgeschalteter Geräte). Neben den ~26 Shellys hängen dort **P750W** (Drucker),
|
||
**Airfryer** (`espressif`, `.180`), **SwitchBot floor-lamp**, **Twinkly** `.228`, **DTUBI** `.8`,
|
||
**SONOFF-NSPanelPro** und ein **iPhone** `.177`. ⇒ Ein Passwortwechsel ist heute eine
|
||
Batch-Aktion über ~26 Geräte, und **das ist genau die Rechnung, die TP17.1 verkleinert.**
|
||
- 🔧 **Zwei Umsetzungshinweise fürs spätere Rotations-Paket:**
|
||
**(a)** `mcp/shelly/migrate.py` hat **kein** Rotate-Kommando (nur `migrate`/`deanchor`/`auth`/
|
||
`table`) — es bräuchte eine kleine Variante über denselben `set_sta`-Pfad.
|
||
**(b) Ein Flag-Day ist vermeidbar:** RouterOS 7 kann in `/interface wifi access-list`
|
||
**Passphrase-Overrides je MAC**. Also neuer PSK aufs Profil, Altgeräte übergangsweise per MAC mit
|
||
dem alten weiter, danach die Liste leeren. 💡 Nebeneffekt: der TP25-Befund „keine `access-list`"
|
||
bekäme damit endlich einen Zweck — und seit dem 08-23 gibt es die ComplianceRule
|
||
`wifi-access-list`, die Einträge dort auch sichtbar macht.
|
||
- **Mitzuziehen bei der Rotation** (legitime, gitignorete Ablagen desselben Werts):
|
||
`~/.secrets/credentials.env` (+ `.bak-20260808`), `routeros/.secrets/wlan-psks.env`,
|
||
`mcp/shelly/.env`.
|
||
|
||
- **CF-Access-Service-Token** — **Rotationsposten #4**, am 2026-08-24 aus den Follow-ups hierher
|
||
gezogen (stand dort als „TP14.4 — CF-Access-Service-Token rotieren"). Sachlich gehört er zu
|
||
**TP14.4** (CF-Access-Profil für Claude, Runbook `docs/tp14.4-cf-access-token-runbook.md`); hier
|
||
steht er, damit die Rotationsliste vollständig ist. ⚠️ Der `CF_API_TOKEN` läuft ohnehin
|
||
**2027-01-09** ab — spätestens dann ist dieser Posten fällig, unabhängig vom Projektende.
|
||
|
||
#### Posten #3 im Detail — Shelly-Gerätepasswort (`SetAuth`), vormals TP17.2
|
||
*Am 2026-08-24 als eigenes TP aufgelöst und hierher gezogen — Text unverändert übernommen.*
|
||
> 🔴 **Hochstufung 2026-08-28:** `SHELLY_DEVICE_PW` ist im Zuge des shelly-mcp-Deploys (Session B)
|
||
> erneut in ein Sitzungstranskript geraten — `sed 's/=.*/=<set>/'` auf der Stack-YML hat den Wert
|
||
> **nicht** maskiert (Replacement-Pattern zu wenig greedy). Datei selbst ist gitignored/chmod 600,
|
||
> aber das Transkript ist die Expositionsklasse. Folge: Vorziehgrund, **Posten #3 ist ab jetzt der
|
||
> dringendste offene Rotationspunkt** — vor #2 (PSK, der schon länger im Klartext in der Git-Historie
|
||
> steht) und #1 (MikroTik `admin`, der schon zwei Transkript-Leaks hat, aber beide male dasselbe PW,
|
||
> nicht **vier** verschiedene Quellen wie hier). Reihenfolge: erst **Werkzeug-Härtung**
|
||
> (`sed`-Replacement absichern ODER `tr`-Stream-Filter vor jedem Read einer Secret-Datei),
|
||
> dann Posten #3 rotieren (alle 23 Shellys + HA-Re-Auth-Flow).
|
||
|
||
> **User 2026-08-23:** „außerdem haben wir einen punkt, um die shellys mit einem pwd zu schützen."
|
||
> Damit ist der Zurückgestellt-Status von 2026-07-13 aufgehoben. **Begründung der Kopplung:** die
|
||
> PSK-Rotation fasst ohnehin jeden Shelly an und rebootet ihn — `SetAuth` gehört in denselben
|
||
> Durchgang, ein Reboot statt zwei.
|
||
>
|
||
> ✅ **Werkzeugseitig ist nichts zu bauen:** `mcp/shelly/migrate.py` hat `cmd_auth` (setzt
|
||
> `Shelly.SetAuth` mit SHA-256-`ha1` gegen die Geräte-`realm`, Dry-Run als Default), und `_rpc` hat
|
||
> einen **Digest-Auth-Fallback** auf 401 mit `SHELLY_DEVICE_PW`.
|
||
>
|
||
> 🔴 **Der teure Teil ist nicht `SetAuth`, sondern Home Assistant.** Mit aktiver Auth verlangt die
|
||
> Shelly-Integration je Gerät einen **Re-Auth-Flow** — bei 23 Geräten ist *das* der Aufwand, und genau
|
||
> der Grund für die frühere Einstufung als „Kosmetik". ⇒ **Erst an EINEM Gerät machen und den
|
||
> HA-Aufwand messen** (Vorschlag `s15 technik_pve`, RSSI −36, unkritisch), bevor der Rest folgt.
|
||
> Sonst bündelt man ein ungemessenes Risiko in eine 23-Geräte-Aktion.
|
||
|
||
<details><summary>Ursprünglicher Text (historisch)</summary>
|
||
|
||
- **Was:** je Gerät `Shelly.SetAuth` (User `admin`, realm-individueller `ha1` = sha256("admin:<device-id>:<pw>"), einheitliches Klartext-PW) via `mcp/shelly/migrate.py auth --go`. `SHELLY_DEVICE_PW` liegt in `mcp/shelly/.env` (gesetzt, len 13). Runner + Digest-Auth-Fallback in `_rpc` fertig.
|
||
- **Warum zurückgestellt:** rein kosmetisch/Härtung; **bricht die HA-Shelly-Integration**, bis pro Config-Entry (22×) das Credential neu eingetragen ist → disruptiv, kein Mehrwert im abgeschotteten IOT-VLAN. **Erst nach** dem Netz-Teardown (TP19) neu bewerten.
|
||
|
||
</details>
|
||
|
||
#### 🔴 Neues Leak-Ereignis 2026-08-24 — der IoT-PSK ist erneut exponiert
|
||
Beim 5-GHz-Umfeldscan für TP25 wurde `mcp__mikromcp__list_wifi_interfaces` gegen den Manager gefahren,
|
||
um die `cap-wifiN`-Zuordnung zu bestimmen. **Das Tool gibt `security.passphrase` aller Profile im
|
||
Klartext zurück** — damit stehen **alle fünf WLAN-Passphrasen** (`main`, `iot`, `media`, `untrusted`,
|
||
`guest`) in einem Sitzungstranskript. Kein Werkzeug hat versagt; es war das falsche Werkzeug für eine
|
||
Kanalabfrage. **Betrifft Posten #2 (IoT-PSK) und zusätzlich vier bisher unverbrannte Passphrasen.**
|
||
⚖️ **Nach der „harten Ausnahme" oben ist das ein Vorziehgrund** — Entscheidung gehört Christoph.
|
||
Gegenargument, das dieselbe Rechnung wie am 08-23 ergibt: der IoT-PSK lag ohnehin schon im Klartext
|
||
in der Git-Historie, neu exponiert sind vor allem `main`/`media`/`untrusted`/`guest` — und die kosten
|
||
beim Rotieren **wenige** Clients, nicht 26. ⇒ **`main` und `guest` wären billig und einzeln machbar,
|
||
unabhängig vom gebündelten Durchgang.**
|
||
|
||
#### Fallen, die für jedes Setzen/Rotieren gelten (nicht neu erheben)
|
||
- 🔴 **Kein Read auf `/interface/wifi`, wenn nur Kanäle/Interfaces gebraucht werden** —
|
||
`list_wifi_interfaces` liefert alle Passphrasen im Klartext mit (Leak vom 2026-08-24, s.o.).
|
||
Feldauswahl benutzen oder `/interface wifi channel print detail`.
|
||
- 🔴 **Niemals `sshpass -p`** — schreibt das Passwort in die Kommandozeile, sichtbar in `ps` und in
|
||
jedem argv-Trace. **Genau so entstand Leak #1.** `-e` hält es in der Umgebung des Kindprozesses.
|
||
Der Repo-Stand ist bereits durchgängig `-e`.
|
||
- 🔴 **Passwörter nie per MCP-`password`-Parameter setzen**, sondern per Skript aus `~/.secrets`
|
||
(`manage_user set-password`; `/user set*` ist per Deny-Policy blockiert).
|
||
- 🔴 **Keine Secrets-Enumeration im Transkript** — das war Leak #2, ohne dass irgendein Werkzeug
|
||
fehlerhaft war.
|
||
- 🔒 **`wifi-security` ist als ComplianceRule grün, aber NICHT deploybar** (Passphrasen stehen als
|
||
`<<REDACTED>>` im Template). Ist-Stand für #2 immer per REST vom Gerät lesen
|
||
(`GET http://10.40.99.1/rest/interface/wifi/security`), **nie** aus `wlan-psks.env`.
|
||
- ⚠️ **CAPsMAN, für #2:** eine Profiländerung wirkt erst nach `remote-cap provision` für **beide**
|
||
APs — und die Abnahme ist eine **frische DHCP-Lease je SSID**, nicht die Registration-Table.
|
||
- ⚠️ **Für #2 zuerst `multi-passphrase` gegen WPA3/SAE testen — auf einer Wegwerf-SSID, nie an
|
||
`dirtyvader-iot`.** `sec-iot` fährt `wpa2-psk,wpa3-psk`; trägt multi-passphrase SAE nicht, sperrt
|
||
man **26 IoT-Geräte auf einmal** aus, inklusive Nuki und NSPanel.
|
||
- ⚠️ **`s1`/`s2`/`s3` sind ausgesteckt** und kommen im Dezember mit dem **alten** PSK zurück — der
|
||
Übergangs-Eintrag muss sie überleben oder sie werden bei Rückkehr von Hand nachgezogen.
|
||
- 🔴 **HA- und Repo-Nummerierung der Shellys weichen ab** (`s10`, `s17`) — **immer über MAC/IP
|
||
zuordnen, nie über die s-Nummer.**
|
||
- **Abh.:** keine harten; #2 und #3 laufen sinnvollerweise in **einem** Geräte-Durchgang.
|
||
**Blockiert nichts.** Die frühere Kopplung „erst TP17.1, dann rotieren" ist **gegenstandslos** —
|
||
TP17.1 ist am 2026-08-23 verworfen, die Geräte bleiben WLAN-Clients.
|
||
|
||
|
||
<a id="tp31"></a>
|
||
### TP31 — Reverse Proxy für innen und außen; cloudflared von sechs Knoten auf einen ▸ 🔜 **GEPLANT 2026-08-25**
|
||
> **Auslöser (User 2026-08-24):** „ich weigere mich, für public applications 2 cloudflare container zu
|
||
> installieren. … ich habe [den Core] unter der prämisse gekauft, dass über diesen dann auch die public
|
||
> apps laufen können. wir wollten doch den core als single point of failure platzieren."
|
||
|
||
#### Der Vorwurf, fair geprüft
|
||
Eine schriftliche Zusage, dass die Public-Apps auf den Core wandern, existiert **nicht** —
|
||
[TP16.1](teilprojekte-archiv.md#tp16-1) ist durchgehend auf den *privaten* Zugang zugeschnitten und hält
|
||
mehrfach fest, dass „der cloudflared-Public-Teil unberührt blieb". **Aber der Vorwurf trifft trotzdem
|
||
etwas Richtiges:** dasselbe Dokument begründet die gesamte Auswahl mit **Christophs** Prinzip —
|
||
*„Router = einziger SPOF, keine Abhängigkeit zu anderen Rechnern"* — und **niemand hat je geprüft, ob der
|
||
öffentliche Strang ohne sechs Container auskommt.** Keine Täuschung, aber eine **ungeprüfte Annahme**.
|
||
|
||
#### Die Lösung ist nicht „raus aus Cloudflare", sondern „ein Knoten statt sechs"
|
||
**Caddy** als Reverse Proxy für **innen und außen**, `emsenhuber.net` in beiden Richtungen, plus **ein**
|
||
cloudflared als Tunnel-Endpunkt.
|
||
|
||
- ⚖️ **Erst geprüft, dann verworfen: der Weg über `dst-nat`.** Der Core hält `90.146.33.177` direkt ohne
|
||
CGNAT, Liwest lässt eingehend alles durch (User: „hatte früher schon nginx hier laufen"), und
|
||
Origin Rules (Ziel-Port, **Free = 10 Regeln**), die CF-HTTPS-Ports (`443, 2053, 2083, 2087, 2096, 8443`)
|
||
sowie Authenticated Origin Pulls sind alle im Free-Tarif enthalten. Es **ginge** also ohne jeden Container.
|
||
- 🎯 **Christophs Einwand ist der bessere Weg:** *„caddy muss so oder so. nachdem dieser vorhanden ist,
|
||
fahren wir natürlich über zero trust apps. noch etwas sicherer, aber kein zusätzlicher knotenpunkt,
|
||
der ausfallen kann."* Der Tunnel spart drei Risiken auf einmal: **keine angreifbare Origin-IP, keine
|
||
CF-IP-Allowlist, kein DDNS als Voraussetzung** (der TP10-Updater bliebe sonst zwingend).
|
||
|
||
#### Zielbild
|
||
```
|
||
Internet ─► Cloudflare (Access, WAF) ─► Tunnel »bf-public« ─► cloudflared auf ap1/ap2
|
||
WARP ─► cf-mesh (nativ am Core, kein Container) ─┐
|
||
LAN ─► Split-Horizon-DNS ───────────────────────┴─► Caddy (LXC 130, 10.40.20.40)
|
||
└─► alle Dienste per Host-Header
|
||
```
|
||
**Öffentlich bleiben vier:** `vw` (Bitwarden-Clients), `hass` (Companion-App), `n8n` (Webhooks Dritter),
|
||
`gitea` (Access **nur für die Web-UI**, Git-/API-Pfade daran vorbei ⇒ **Push ohne WARP bleibt**).
|
||
Alles andere ist intern; **19 Ingress-Zeilen über drei Tunnel werden EINE Catch-all-Regel auf Caddy.**
|
||
|
||
#### Wo cloudflared läuft — und warum die APs (live gemessen 2026-08-24/25)
|
||
|
||
| Gerät | Arch | Flash frei | RAM frei | Container? |
|
||
|---|---|---|---|---|
|
||
| **ap1 / ap2** (hAP ax²) | **arm64** | **95 MB / 128 MB** | **690 MB / 1 GB** | ✅ ja |
|
||
| **crs326-core** | arm **32-bit** | **2,9 MB / 16 MB** | 512 MB | ❌ **nie** |
|
||
|
||
- ✅ **Christophs Argument, das den Ausschlag gibt:** *„cf auf den aps hat den vorteil, dass wir gegenüber
|
||
unseren hosts/container nicht blind sind."* — Der AP ist eine **eigene Maschine**, kein Docker-Host;
|
||
die macvlan/ipvlan-Blindheit kann dort **prinzipiell** nicht auftreten. Kein anderer Ort hat das.
|
||
💡 Daraus folgt eine Reserve: fällt Caddy aus, kann derselbe Connector über eine zweite Ingress-Regel
|
||
**direkt** auf ein, zwei kritische Backends zeigen.
|
||
- 🔴 **Ehrliche Einschränkung:** cloudflareds einziges Origin ist Caddy. Solange Caddy auf pve liegt,
|
||
kauft ein Connector auf dem AP **Hardware-Diversität, aber keine Verfügbarkeit** — fällt pve aus, ist
|
||
alles weg. Der echte Gewinn kommt erst mit `caddy-bf2` (LXC 131).
|
||
- **Caddy oder AGH zusätzlich auf die APs? Nein.** 95 MB minus RouterOS-Reserve fürs eigene `.npk`
|
||
(~20 MB) = **ein Container pro AP**. Kriterium, das auch künftig trägt: **auf die Infrastruktur darf,
|
||
was sich selten ändert und wenig schreibt** — cloudflared ja (Config liegt in Cloudflare, schreibt
|
||
nichts); **Caddy nein** (Caddyfile ändert sich bei jedem neuen Dienst); **AGH nein** (Query-Log
|
||
schreibt dauernd → NAND-Verschleiß).
|
||
- ⚠️ **Preis:** `container` verlangt eine **`device-mode`-Änderung mit physischer Bestätigung** am Gerät
|
||
(ap1 Technik UG, ap2 Technik OG). Und der AP bekommt eine zweite Rolle neben dem WLAN.
|
||
|
||
#### Umsetzung
|
||
1. ✅ **ERLEDIGT 2026-08-25 — Leichen gelöscht:** `120 adguard-bf1`, `121 adguard-bf2`,
|
||
`128 nginx-proxy-manager`, `129 caddy`, `130 pihole1`, `131 pihole2` — alle waren `stopped`,
|
||
`onboot: 0` und hingen konfigurativ noch am **toten VLAN1** (`10.40.41.116/.117`, GW `.41.254`).
|
||
`pct destroy --purge` hat `120` und `128` zugleich aus dem Backup-Job entfernt.
|
||
**Alle 72 PBS-Snapshots radiert** — sieben davon waren in PBS als **`protected`** markiert und
|
||
mussten erst über `pvesh set …/content/<volid> --protected 0` entschützt werden
|
||
(User-Freigabe 2026-08-25). Gegenprobe: **0 Snapshots** zu allen sechs IDs.
|
||
⚠️ **Merkregel:** `pvesm free` scheitert an geschützten Snapshots mit *„cannot remove protected
|
||
snapshot"* — und zwar **stillschweigend**, wenn man den Fehler nicht ausliest. Wer nur die
|
||
Erfolgszähler betrachtet, hält ein halb erledigtes Aufräumen für fertig.
|
||
2. ✅ **ERLEDIGT 2026-08-25 — LXC 130 `caddy-bf1` steht:** Debian 13, VLAN20 `tag=20`,
|
||
**`10.40.20.40/24`**, GW/DNS `10.40.20.1`, **`onboot: 1`**, **`startup order=10`** (Core-Stufe
|
||
nach TP32), 4 GB / 512 MB, 2 Cores. In den Backup-Job `backup-6dcf5c10-610f` aufgenommen.
|
||
**Caddy v2.11.4** installiert, Modul **`dns.providers.cloudflare`** verifiziert vorhanden.
|
||
Egress geprüft (DNS, HTTPS, CF-API) — TP5 lässt VLAN20 nach draußen.
|
||
3. ✅ **ERLEDIGT 2026-08-25 — Token da, `deploy.sh` durchgelaufen, Proxy verifiziert.**
|
||
**`caddy/Caddyfile` im Repo** (17 Sites: 4 öffentlich `vw`/`hass`/`n8n`/`gitea`, 13 intern inkl.
|
||
der vier MCP-Endpunkte und drei HTTPS-Backends mit `tls_insecure_skip_verify`), Syntaxprüfung
|
||
läuft durch. Zertifikate ausschließlich per **DNS-01**, damit auch rein interne Namen gültige
|
||
Zertifikate bekommen.
|
||
🔑 **Das eng geschnittene Token liegt** — `Zone:DNS:Edit`, Zone-Resource auf `emsenhuber.net`
|
||
begrenzt, **nicht** das account-weite `CF_API_TOKEN` (Fingerprint gegengeprüft). Ablage als
|
||
`CF_DNS_API_TOKEN` in `~/.secrets/credentials.env`, Ausrollen per **`caddy/deploy.sh`** — das
|
||
liest den Wert von dort und schreibt ihn in die Container-Env, **ohne ihn je auf stdout oder in
|
||
eine Kommandozeile zu bringen** (dieselbe Regel wie `sshpass -e`).
|
||
4. 📋 **Split-Horizon → ENTSCHIEDEN 2026-08-25: in AdGuard, zusammen mit TP23 Stufe 2.**
|
||
➡️ **Übergabe steht (2. Fassung, 08-25 spät):**
|
||
[`handoff-20260825-agh-dns-cutover.md`](handoff-20260825-agh-dns-cutover.md) — mit drei live
|
||
erhobenen Funden, die den ersten Plan korrigieren: **(a)** der Bestands-AGH `10.40.20.16` ist ein
|
||
**Archiv, keine Vorlage** (24 Rewrites: 8 ins tote VLAN1, 9 auf den heute zerstörten NPM, 2
|
||
FritzBox-Reste) ⇒ Upstreams ja, Rewrites neu; **(b)** `10.40.20.17` ist **`adguard_fm2`**, gehört
|
||
FM und antwortet ebenfalls ins tote VLAN1 — BF hat heute **einen** AGH, keine Redundanz;
|
||
**(c)** die TP5-Vorratsregeln zeigen hart auf `10.40.20.16` — nach dem DHCP-Flip wird DNS zu einem
|
||
**`forward`-Fluss über VLAN-Grenzen**, ohne erweiterte Regeln verlieren IOT/MEDIA/UNTRUSTED/GUEST/MGMT
|
||
die Auflösung, während VLAN20 unauffällig weiterläuft. Dazu: `lease-time=1d` macht Flip **und**
|
||
Rollback träge, Rückfallebene bleibt der Core.
|
||
Begruendung des Users: nichts zweimal bauen. **Konsequenz, die man kennen muss:** AdGuard wird
|
||
damit vom Nebendarsteller zum **Resolver des Standorts** — heute liefert der DHCP in **allen
|
||
sieben VLANs** `dns-server = <VLAN>.1`, also den Core; AGH auf `10.40.20.16` bekommt niemand.
|
||
⇒ Der Schritt ist groesser als „ein paar Eintraege": er verschiebt die DNS-Aufloesung des ganzen
|
||
Hauses vom grundsoliden Core in einen Container.
|
||
🔑 **Deshalb als Paar bauen** — TP32 reserviert `110`/`111` fuer DNS, und die geloeschten
|
||
Altlasten hiessen nicht zufaellig `adguard-bf1`/`adguard-bf2`: die Redundanz war urspruenglich
|
||
schon einmal geplant. **Ein einzelner AGH waere ein DNS-SPOF auf pve.**
|
||
5. **Tunnel `bf-public`** + Connectoren auf ap1/ap2, CNAMEs umhängen (`n8n` → `gitea` → `hass` → `vw`).
|
||
6. **Abbau:** `bf-nas`/`bf-pve`/`bf-mm` löschen, sechs Container weg, SSH-Browser-Rendering entfällt,
|
||
**MCP-Clients auf FQDN umstellen** (`~/.claude.json` spricht heute `10.40.20.105:9583` und
|
||
`10.40.20.173:3000` roh; `n8n-mcp`/`gitea-mcp` kommen dabei wieder in Betrieb).
|
||
|
||
#### ✅ Stand 2026-08-25 nach dem Bau — Caddy laeuft, zwei Backends fielen dabei auf
|
||
**17 von 17 Zertifikaten** per DNS-01 von Let's Encrypt geholt, Dienst `active`+`enabled`,
|
||
Proxy-Funktion gegen den Host-Header verifiziert: `nautobot` 302 · `gitea` 200 · `pve` 200 ·
|
||
`portainer` 200. `caddy/deploy.sh` laeuft End-to-End (Token wird aus `~/.secrets` gelesen, per
|
||
Fingerprint gegengeprueft: **eigenes Token, nicht das account-weite**).
|
||
|
||
🔴 **Fund: `grafana` war seit Langem tot — und Cloudflare Access hat es maskiert.**
|
||
LXC 104 `alpine-grafana` stand auf `stopped` und konfigurativ noch auf **VLAN1**
|
||
(`10.40.41.109`); die CF-Ingress-Zeile zeigte auf `10.40.20.109:3000` — eine Adresse, die es fuer
|
||
Grafana **nie gab**. Im FQDN-Smoke-Test lieferte `grafana.emsenhuber.net` trotzdem ein sauberes
|
||
**302** — das war der **Access-Login**, nicht der Dienst.
|
||
⚠️ **Das ist exakt die Falle aus TP16.3 §8, diesmal in freier Wildbahn:** ein Statuscode vom
|
||
oeffentlichen FQDN sagt nichts ueber das Origin, solange Access davor steht. **Ein toter Dienst
|
||
sieht monatelang gesund aus.** ⇒ Caddy-Zeile entfernt (16 Sites), **LXC 104 zerstoert** und aus dem
|
||
Backup-Job genommen. ~~Die 14 PBS-Snapshots bleiben bewusst stehen.~~ **Am 2026-08-25 auf
|
||
User-Entscheid ebenfalls weg** — zusammen mit den vier gestoppten Gaesten `103 minarca`,
|
||
`109 node-red`, `115 hms-mqtt-publisher` und `201 openproject` (28 Snapshots). **Damit laeuft auf
|
||
pve jeder existierende Gast**, 11 LXC + 2 VMs, kein `stopped` mehr. ⚠️ **Snapshot-Liste vor dem
|
||
`destroy` einsammeln** — `pvesm list --vmid` meldet danach 0, obwohl die Backups noch liegen** — direkt gegen den Datastore gegengeprueft (`ct/104` = 0), nicht nur
|
||
gegen `pvesm list --vmid`, dessen Filter nach dem `destroy` nicht mehr aussagekraeftig ist. Wiederaufbau spaeter als eigener Punkt, dann nach TP32-Systematik.
|
||
|
||
#### ✅ Abnahme 2026-08-25 (spaet) — 16 von 16 Sites gegen den Host-Header gemessen
|
||
Dienst `active`+`enabled`, Probe von einem **zweiten Host** (pve, VLAN20) mit
|
||
`curl --resolve <fqdn>:443:10.40.20.40`, also ueber den echten Namen statt ueber die IP:
|
||
**11x 200** (`vw` `n8n` `gitea` `ds-bf` `ccu` `iobroker` `z2m25` `n8n-mcp` `pve` `portainer` `pbs`),
|
||
`nautobot` **302** (Login-Redirect, erwartet), Antwortzeiten 0,02–0,11 s.
|
||
- ✅ **Die drei „Fehlercodes" der MCP-Endpunkte sind KEINE:** `pve-mcp` 400, `ha-mcp` 403,
|
||
`gitea-mcp` 404 kommen **identisch auch direkt vom Origin** (gegengemessen ohne Caddy). Es sind die
|
||
Antworten der MCP-Server auf ein nacktes `GET /` — der Proxy reicht sie unveraendert durch.
|
||
⇒ **Merkregel: einen Statuscode nie ohne Gegenprobe am Origin als Proxy-Fehler lesen** — sonst
|
||
jagt man einen Fehler, der keiner ist (dieselbe Klasse wie der `grafana`-Fall eine Zeile hoeher,
|
||
nur andersherum).
|
||
- 🧹 **Rest: ein verwaistes Zertifikat.** Der Store haelt **17** Zertifikate, die Config nur 16 Sites —
|
||
`grafana.emsenhuber.net` liegt noch da. Harmlos (Caddy erneuert es nicht mehr), beim
|
||
Grafana-Wiederaufbau nach TP32 mit aufraeumen.
|
||
|
||
✅ **`hass` liefert 200 — gelöst 2026-08-26, und die Ursache ist eine Falle für jeden Reverse Proxy.**
|
||
Der `http:`-Block in `configuration.yaml` war **wirkungslos**: Home Assistant hat die http-Konfiguration
|
||
nach **`.storage/http`** migriert (`"yaml_migration_done": true`, `created_at` 2026-08-05). Seither wird
|
||
der YAML-Block **ignoriert** — ohne Fehlermeldung.
|
||
- 🔎 **Warum das so schwer zu sehen war:** die Logzeile lautet `Received X-Forwarded-For header from an
|
||
untrusted proxy 10.40.20.40` und **nicht** „not set-up for reverse proxies". Laut Quelltext fällt die
|
||
erste nur bei **aktivem** `use_x_forwarded_for` — was zur Annahme führt, die Option greife und nur die
|
||
Liste sei falsch. Tatsächlich kam beides aus `.storage`: `use_x_forwarded_for: true` stand dort längst,
|
||
die Liste enthielt `.20.10`, `.20.126`, `.20.167` (die drei cloudflared-Origins) — nur Caddy fehlte.
|
||
- 🧪 **Was den Verdacht in die Irre führte, der Reihe nach ausgeschlossen:** Datei-Identität (ein
|
||
Testskript in `python_scripts/` tauchte sofort als Service auf ⇒ HA liest dieses Verzeichnis),
|
||
Blockposition (nach ganz oben verschoben — keine Änderung), Kommentare/Formatierung (minimaler Block —
|
||
keine Änderung), Prozesszeitpunkt (`.ha_run.lock` `start_ts` gegen die Datei-`mtime` gestellt) und
|
||
Schema-Validierung (ein absichtlich ungültiger Schlüssel im `http:`-Block erzeugte **keinen**
|
||
Config-Fehler — im Nachhinein der eigentliche Beweis).
|
||
- ✅ **Der vorgesehene Weg ist ein WS-Zweischritt mit Auto-Revert:** `http/config` lesen →
|
||
`http/config/configure` mit der **vollständigen** Config (setzt `pending` und **startet HA neu**) →
|
||
innerhalb von **5 Minuten** `http/config/promote`, sonst fällt HA automatisch auf `stable` zurück.
|
||
⚠️ Der Promote ist knapp: HA braucht hier ~2 min bis zur Erreichbarkeit, und der WS-Weg über das
|
||
MCP-Add-on antwortet währenddessen mit 502. **Erst promoten, wenn die Wirkung gemessen ist** — genau
|
||
dafür ist das Fenster da.
|
||
- 🧹 In `configuration.yaml` steht jetzt ein Hinweis-Kommentar statt eines wirkungslosen Blocks.
|
||
- ✅ **Trusted-Liste aufgeräumt (User, 2026-08-26):** `10.40.41.10/32` — die alte VLAN1-NAS — ist
|
||
entfernt und der Stand als `stable` bestätigt. Vertraut wird jetzt genau dem, was auch wirklich
|
||
proxyt: `10.40.20.40` (Caddy) sowie `.20.10`/`.20.126`/`.20.167` (die cloudflared-Origins, die mit
|
||
Schritt 6 wegfallen), dazu Loopback und `172.16.0.123`. Abnahme: `hass`/`vw`/`gitea`/`n8n` je 200.
|
||
|
||
#### 🧭 Wo liegt die Source of Truth? — beantwortet 2026-08-26
|
||
> **User-Frage:** „schaffen wir es mit überschaubarem aufwand cf und caddy synchron zu halten? wo
|
||
> liegt die sot?"
|
||
|
||
**Antwort: gar nicht synchron halten — die Doppelpflege fällt mit Schritt 5/6 von selbst weg.**
|
||
Heute steht dieselbe Zuordnung *Name → Origin* an **zwei** Orten: im CF-Ingress (19 Regeln über drei
|
||
bf-Tunnel) und im `caddy/Caddyfile` (16 Sites). Der Vergleich am 08-26 zeigt: **sie stimmen fachlich
|
||
überein** — jeder gemeinsame Name zeigt auf **dasselbe** Origin. Es gibt also nichts zu versöhnen,
|
||
nur etwas abzuschaffen.
|
||
|
||
Im Zielbild hat der Tunnel `bf-public` **eine einzige Catch-all-Regel auf `10.40.20.40`**. Damit hält
|
||
Cloudflare **keine** Host→Origin-Zuordnung mehr, und die SoT ist eindeutig:
|
||
|
||
| Was | SoT | wo gepflegt |
|
||
|---|---|---|
|
||
| Name → Origin (alle Dienste) | **`caddy/Caddyfile`** (Git) | dieses Repo |
|
||
| Welcher Name ist öffentlich | **CNAME + Access-App** (4 Namen) | Cloudflare |
|
||
| Name → IP **intern** | **AdGuard-Rewrites** (TP23 Stufe 2) | AGH-Paar |
|
||
|
||
🔴 **Standortsuffix als Regel (User-Entscheid 2026-08-26).** Hosts, die es an mehreren Standorten gibt,
|
||
heißen **nie ohne Suffix** (`nas.emsenhuber.net` ist die Falle) — Schema ist
|
||
`<host>.<site>.emsenhuber.net` (`nas.bf.…`, `nas.fm.…`, `nas.dw.…`). Caddy bleibt **pro Standort
|
||
eigenständig** (BF-Caddy kennt `ds-bf`, FM-Caddy kennt `ds-fm`); die AdGuard-Instanzen je Site halten
|
||
die lokale `<host>.<site>`-Auflösung, und ein **gemeinsamer** Name ohne Suffix existiert nicht.
|
||
**Konsequenz für `nas.emsenhuber.net`: gelöscht** — weder AGH-Rewrite, noch CF-CNAME (gab es nie),
|
||
noch Caddyfile-Eintrag (das hatte von Anfang an `ds-bf`). Der Rewrite war eine AGH-Insel ohne
|
||
Auflöser dahinter, der Cutover ändert für **niemanden** etwas an der heutigen Erreichbarkeit.
|
||
**Andere namensgleiche Hosts** (`macmini`/`mfc`/`tv`) stehen heute noch ohne Suffix in AGH —
|
||
Bewertung einzeln in der TP23-Stufe-2-Session; hier nur festgehalten, damit das Muster ab jetzt
|
||
nicht mehr ohne Suffix entsteht.
|
||
|
||
⇒ Zu pflegen bleiben **vier** öffentliche Namen statt neunzehn Ingress-Regeln. Und die zwölf internen
|
||
Namen brauchen dann **gar keinen öffentlichen DNS-Eintrag mehr** — heute ist z. B. `pve.emsenhuber.net`
|
||
öffentlich auflösbar (hinter Access), danach ist es von außen schlicht unbekannt. **Das ist ein
|
||
Sicherheitsgewinn, kein Nebeneffekt.**
|
||
|
||
🛠️ **Werkzeug dafür: [`caddy/cf-sync.py`](../caddy/cf-sync.py)** — read-only, liest CF-Ingress, Zonen-DNS
|
||
und Access-Apps und stellt sie gegen den Caddyfile; Exit 1 bei Drift (cron-/CI-tauglich). Erster Lauf
|
||
2026-08-26 fand **7 echte Abweichungen**, alle in Cloudflare, keine in Caddy:
|
||
- **`grafana` steht noch im Ingress** (`bf-nas → 10.40.20.109:3000`) — ein Origin, das es nie gab.
|
||
- **`nautobot` hat noch seinen CNAME**, aber seit TP16.3 keine Ingress-Regel ⇒ läuft in die Catch-all;
|
||
**das** ist die Quelle des 302, das man leicht für „lebt noch" hält. Dasselbe bei `gateway-dw`.
|
||
- **Drei `*.bf`-CNAMEs zeigen auf `nginx.bf`** — den heute zerstörten NPM (`phpmyadmin`, `speedtest`,
|
||
`z2m25`).
|
||
- Eine **Wildcard-Access-App `*.emsenhuber.net`** deckt die ganze Zone ab — sie ist der Grund, warum
|
||
jeder FQDN ein sauberes 302 liefert, auch wenn dahinter nichts läuft.
|
||
🔑 **Merkregel daraus:** *ein Eintrag ohne Gegenstück ist unsichtbar, solange etwas davor antwortet.*
|
||
Der Drift-Check muss deshalb **beide** Richtungen prüfen — Ingress ohne Site **und** CNAME ohne Ingress.
|
||
|
||
#### HA-Ausblick (nicht Teil der Umsetzung)
|
||
**Es bleibt bei Caddy** — zustandslos bis auf den Zertifikatsspeicher, identisches Caddyfile je Instanz,
|
||
versionierbar; NPM hätte eine Datenbank, die man mitreplizieren müsste.
|
||
🔴 **Prinzip für `vw`: der Eingang darf nicht klüger sein als die Datenbank.** [TP20.1](#tp20-1) sieht
|
||
**manuelles Promote** vor. Zwei Connectoren auf **verschiedene** Caddys an verschiedenen Standorten
|
||
würden die Hälfte der **Schreib**zugriffe auf einen read-only Standby schicken — das #1033-Muster,
|
||
diesmal mit Datenverlust statt 530ern. **Automatisches Ingress-Failover erst, wenn der DB-Teil es trägt**
|
||
(User: vertagt bis TP20.1 Phase 1+2 laufen).
|
||
|
||
- **Abh.:** [TP32](#tp32) (Nummern/IPs — vergibt `130`/`.40`), TP16.1 ✅ (Mesh), **TP23 Stufe 2**
|
||
(Split-Horizon-Ort). **Löst ab:** TP16 (reziprokes HA) und **TP16.2** (der `vw`-bypass-Strang bekommt
|
||
mit Caddy + CF davor einen anderen Schutz). **TP16.3 ist der Pilot dieses TP.**
|
||
- 📋 **Vollständiger Plan mit allen Abwägungen:** `~/.claude/plans/expressive-pondering-clarke.md`
|
||
|
||
<a id="tp32"></a>
|
||
### TP32 — Nummern- und Adress-Systematik ▸ 🟢 **KONZEPT STEHT (2026-08-25), Startreihenfolge bereits repariert**
|
||
> **User-Auftrag 2026-08-24:** „du/ich sollst in der lage sein, selbstständig nach einem vorgegebenen
|
||
> system freie ips vergeben zu können … und diese systematik findet sich in anderen netzen künftig auch
|
||
> wieder" · „ich meine 100 sollte künftig für die core services stehen."
|
||
>
|
||
> **Eigener TP, weil es eine Konvention ist, kein Bauteil** — TP31 ist ihr erster Kunde.
|
||
> **Regel: Bestand bleibt, wo er ist; jeder NEUE Gast folgt der Systematik.**
|
||
|
||
🔑 **Ein gemeinsamer VMID-Raum für VMs *und* LXCs.** `100 haos` und `330 RaspberryMatic` sind **VMs** —
|
||
die Klasse bestimmt die Nummer, nicht die Virtualisierungsart.
|
||
|
||
| Kreis | Klasse | heute dort (VM *kursiv*) |
|
||
|---|---|---|
|
||
| **100–199** | **Core / Infrastruktur** | `108 mcast-relay` |
|
||
| **200–299** | **Daten / DBs** (Precious-Tier) | *(`105`, `106`, `107`, `302` → später hierher)* |
|
||
| **300–399** | **Security / Identity** | — |
|
||
| **400–499** | **Platform / Runtime** | ✅ `400 docker` passt bereits |
|
||
| **500–599** | **App-Services / Smart Home** | `101`, `102`, `112`, `150`, *`100`*, *`330`* → später hierher |
|
||
| **600–699** | **Business / ERP** | `201 openproject` |
|
||
| **700–799** | **KI / ML** | — |
|
||
| **800–899** | **Dev / R&D / Test** | — |
|
||
| **900–999** | **Ephemeral / Scratch** | wird ungefragt gelöscht |
|
||
|
||
**Zehnerschritte innerhalb eines Kreises** („Respektabstand", User), damit Instanzen und Nachbarn Platz
|
||
haben: `100–109` netznah · **`110–119` DNS** · **`130–139` Reverse Proxy** · `150–159` Monitoring ·
|
||
`170–179` PKI/Zeit. ⇒ **AGH `110`, Caddy `130`.**
|
||
|
||
**Letztes Oktett je /24 — gilt in jedem VLAN, an jedem Standort:**
|
||
|
||
| Oktett | Rolle | Anker heute |
|
||
|---|---|---|
|
||
| `.1–.9` | Gateways / L3 | `.1` je VLAN ✅ · `99.1` core, `99.2` branch ✅ |
|
||
| `.10–.29` | physische Hosts (Storage, Hypervisor, APs) | NAS `20.10` ✅ · APs `99.11/.12` ✅ |
|
||
| `.30–.99` | virtuelle Core-Services, Zehnergruppen wie oben | DNS `.3x` · Ingress `.4x` · DBs `.5x` · Security `.6x` · Monitoring `.7x` |
|
||
| `.100–.199` | App-Services | |
|
||
| `.200–.249` | DHCP-Pool | |
|
||
| `.250–.254` | VIPs / Relays | |
|
||
|
||
🔑 **Der Merk-Trick: die Zehnergruppe im Oktett spiegelt die der VMID.** `110 agh` → `.3x`,
|
||
`130 caddy` → `.4x`. Wer eine Zahl kennt, kennt die Klasse der anderen.
|
||
|
||
#### Warum der Bestand anders aussieht — er war nicht falsch, nur auf einer anderen Achse
|
||
**User 2026-08-25:** „zuerst war haos, welches mit 100 den nummernkreis für iot eröffnet hat. und die
|
||
anderen … gehörten zu ha, daher 1xx. erst später habe ich erfahren, dass die nummer die
|
||
startreihenfolge definiert."
|
||
⇒ Der Bestand gruppiert nach **Zugehörigkeit**, die neue Systematik nach **Klasse**. Beide Achsen sind
|
||
legitim; die zweite ist die, die das System auswertet. Der alte `1xx`-Block ist der **Smart-Home-Block**
|
||
und wandert später als Gruppe nach `5xx`.
|
||
|
||
#### ✅ ERLEDIGT 2026-08-25: die Startreihenfolge war nachweislich falsch
|
||
🔴 **Und der Hebel ist NICHT die VMID.** Drei Gäste hatten ein explizites `startup: order=`
|
||
(`101 iob-master` 110, `102 mqtt5` 120, `100 haos` 125), alle übrigen keines. **Proxmox startet erst
|
||
alle Gäste mit `order=`, dann den Rest nach VMID** — real lief also `iob-master → mqtt5 → haos` und
|
||
**erst danach** `platform-pg`, `mariadb`, `z2m25`, `docker`.
|
||
⇒ **Home Assistant startete vor seiner eigenen Datenbank.**
|
||
|
||
**Repariert (wirkt beim nächsten pve-Neustart):**
|
||
|
||
| `order=` | Stufe | Gäste |
|
||
|---|---|---|
|
||
| 10 | Core | `108 mcast-relay` |
|
||
| 20 | Daten | `105 platform-pg` (`up=15`), `302 mariadb` (`up=15`), `106 influxdb2`, `107 redis` |
|
||
| 30 | Platform | `400 docker` (`up=20`) |
|
||
| 40 | Broker / Funk | `102 mqtt5` (`up=10`), `112 z2m25`, `330 RaspberryMatic` |
|
||
| 50 / 51 | Anwendungen | `101 iob-master`, **`100 haos`**, `150 iob-exposed` |
|
||
|
||
🎯 **Die wichtigste Erkenntnis des TP: VMID = Klassifikation, `startup: order=` = Abhängigkeitsreihenfolge.**
|
||
Die Umnummerierung muss die Startreihenfolge gar nicht reparieren — das ist bereits erledigt, ohne eine
|
||
einzige ID zu ändern. **Die Nummernkreise sind damit reine Ordnung und Lesbarkeit**, und der Bestand
|
||
kann in Ruhe nachziehen.
|
||
|
||
- **Offen:** Migration des Bestands (Tabelle oben) — *am Ende der Reise, nicht jetzt*. Und die
|
||
Kollision **AGH will `.30`, dort sitzt Nautobot** (`.31` = nautobot-worker) ⇒ Vorschlag `.36` als
|
||
Interim, `.30` erst nach dem Nautobot-Umzug in den App-Bereich. **Fällt vor dem AGH-Neubau an.**
|
||
- **Abh.:** keine. **Blockiert nichts**, aber TP31 vergibt bereits danach.
|
||
|
||
|
||
### TP33 — Sonos aus der Ferne bedienen (VPN/WARP) ▸ 💡 **IDEE, bewusst zurückgestellt** (angelegt 2026-08-25, User-Entscheid „später entscheiden")
|
||
|
||
- **Auslöser:** beim Sonos-Fall vom 08-25 kam die Frage auf, ob die Player auch **von unterwegs**
|
||
auffindbar sein sollen. Antwort für jetzt: **nein, später entscheiden** — hier nur festgehalten,
|
||
damit die Begründung nicht verloren geht.
|
||
- 🔑 **Der Kernsatz:** **SSDP und mDNS queren einen Tunnel nicht von selbst.** Beides ist link-lokal
|
||
(TTL 1 bzw. Multicast auf `224.0.0.251`), ein WireGuard-/WARP-Tunnel ist ein Punkt-zu-Punkt-Interface
|
||
ohne Broadcast-Domäne. Es bräuchte also **dieselbe Relay-Bauart wie TP7.1, nur ein zweites Mal** —
|
||
ein Bein im Tunnel, eines in VLAN40 — plus die passende `forward`-Regel (TP5 ist deny-by-default).
|
||
- ⚖️ **Warum zurückgestellt:** echter Zusatzaufwand für einen **Randfall**. Steuern aus der Ferne geht
|
||
heute schon über die **Sonos-Cloud-App** und über **HA** (`cf-mesh`/CF-Tunnel), beides ohne Discovery.
|
||
Fehlt nur die Desktop-App von außen — dafür lohnt der zweite Relay-Strang derzeit nicht.
|
||
- ⚠️ **Nicht mit dem Gegenteil verwechseln:** **mit aktivem WARP findet die App die Player auch lokal
|
||
nicht** — erwartbares Verhalten, kein Fehler (`ref_warp_local_icmp_trap`, Gotcha in `CLAUDE.md`).
|
||
Wer den Fernzugriff baut, muss genau diesen Fall mit auflösen.
|
||
- **Abh.:** TP7.1 (Relay-Muster) ✅, TP16.1 (`cf-mesh`) ✅. **Blockiert nichts.**
|
||
|
||
---
|
||
|
||
### TP34 — Caddyfile via Nautobot/GC (generische Reverse-Proxy-Schnittstelle) ▸ 🔜 STUB
|
||
> **Konzept + Architektur-Skizze:** [`docs/handoff-20260827-inventur-und-architektur.md`](handoff-20260827-inventur-und-architektur.md) — bitte dort weiterlesen (Architektur-Abschnitt).
|
||
- **Scope:** Reverse-Proxy-Hosts werden im **Nautobot-Datenmodell** gepflegt (Site, Host, Backend-Spec). **Golden Config** rendert den Caddyfile aus den Daten, Diff-Check gegen die Live-Version auf `caddy-bf1`, Compliance-Checks + Transaktionen identisch zum bestehenden Workflow für Router-Configs.
|
||
- **Auslöser (Inventur 2026-08-27):** der Caddyfile (`bf-infra/caddy/Caddyfile`) ist heute **manuell gepflegt** — kein Drift-Check, keine Transaktionen, andere Projekte können keine Sites anlegen ohne Christoph. Lösung: **Nautobot/GC**, kein Caddy-API-Sidecar (kein zweites SSoT).
|
||
- **Erster Anwendungsfall:** `terpene.teilzeitladen.xyz → http://10.40.20.167:8890` (Mac-mini-Backend, dort PostgREST + sync + web via lokalem Caddy `:8890`). Vor TP34: manueller Quick-Win-Caddy-Block.
|
||
- **Abh.:** TP22 (GC-Pipeline) ✅ vorhanden · TP23 (DNS-Policy + Caddy-Voraussetzungen) ✅ Stufe 1. Hängt an **nichts**, eigene Implementierung im Runbook nach dem Quick-Win.
|
||
- **Status:** 🔜 Stub; Konzept + Architektur-Skizze vollständig in Handoff-Datei. Implementierung in eigenem Runbook nach terpene-Caddy-Quick-Win.
|
||
|
||
---
|
||
|
||
### TP35 — MCP-Schichtung global/projekt/user + 3-Schicht-CLAUDE.md + Announcement-Mechanismus + Backup-Guard ▸ ⏳ **ANGELEGT 2026-08-28, Schicht 1 (FQDN→IP) + Schicht 3 (globale Regeln) + Schicht 6 (Skill) ✅**
|
||
- **Auslöser:** Bestandsaufnahme Session 2026-08-28 (Handoff `docs/handoff-20260828-pre-opencode-migration.md`). Erkenntnis: MCP-Configs sind heute über `~/.config/opencode/opencode.jsonc` UND `~/.claude.json` verstreut, ohne klares Schema.
|
||
- **Befund (geprüft):** 11/12 Backups im Verzeichnis `~/dev/.pre-opencode-migration-backups/20260828-150335/` gzip-valid; `uwe-job-bewerbung.tar.gz` korrupt (Source unverändert da). Repos und globale Configs md5-/HEAD-identisch zum Vor-Session-Stand. Synology-Drive-Sync aktiv (Anwender, manuell).
|
||
- **Scope:**
|
||
1. **MCP-Schichtung** (global/projekt/user) — wo lebt welche MCP-Definition, wie wird sie aufgelöst, wie bleibt sie synchron, wenn ein Projekt physisch auf einen anderen Host umzieht. **✅ 2026-08-29 (Teilschritt):** `gitea-mcp` + `n8n-mcp` in der globalen `opencode.jsonc` von CF-FQDN auf LAN-IP migriert (s. Ist-Stand-Block). `cloudflare`-MCP bleibt FQDN (User-Direktive).
|
||
2. **Credentials-Schichtung** — alle Secrets in `~/.secrets/exports.zsh`, MCP-Configs ausschließlich `{env:...}`. Schließt TP30 Posten #1 ab.
|
||
3. **3-Schicht-CLAUDE.md** — `CLAUDE.md` (öffentlich) / `CLAUDE.local.md` (gitignored) / `~/.claude/CLAUDE.md` (global). **✅ 2026-08-29 (globale Schicht):** `~/.config/opencode/AGENTS.md` als SSoT angelegt (Secrets + VLAN-Schema + 5 Lessons + SoC + Übergabe-Mechanik + Mid-Action + Commit-Hinweis + Backup-Guard); `~/.claude/CLAUDE.md` hält denselben Block als Spiegel. **Offen:** die pro-Projekt-Schicht (`CLAUDE.md`/`CLAUDE.local.md` + Handover-Posts) — eigene Sessions.
|
||
4. **Announcement-Mechanismus (Hybrid)** — zentraler Ort (voraussichtlich `bf-infra/docs/announcements.md`) + Skill `/news` oder `/announce` zum Einlesen beim Session-Start. Initial: Breaking News über die MCP-Schichtung selbst.
|
||
5. **Backup-Guard** — `~/dev/.bin/backup-guard.sh`, Pre-Commit-Hook pro Projekt, „3 Tage aktiv"-Definition: 5 Commits oder 1000 LOC diff.
|
||
6. **`/projektstatus`-Skill global** — als Erstes, vor den anderen Schichten. Alle Projekte verwenden dasselbe Skill. **✅ 2026-08-28:** `/projektstatus`-Skill nach `~/.config/opencode/skills/projektstatus/SKILL.md` kopiert (`cp`, nicht `ln -s`) — opencode scannt diesen Default-Pfad, kein `skills.paths`-Eintrag in der JSONC nötig. Lehre im CLAUDE.md-Lessons-Block.
|
||
- **Abgrenzung:** TP35 ist Konzept + Schichtung, nicht die Umsetzung. Pro Projekt eigene Session, eigener Handover-Post, eigene Mini-Anpassung.
|
||
- **Verzahnt mit:** TP30 (Credentials), TP34 (Repository-Pattern), TP22 (Nautobot-GC als Vorbild für SSoT + Compliance), TP11 (Monitoring).
|
||
- **Erste Session für Scope-Ausarbeitung:** Schichten 1–5 + Skill-Setup; Handover-Posts pro Projekt (Schicht 3 + 4 ergeben die Posts).
|
||
- **Referenzen:** Handoff 2026-08-28 (`docs/handoff-20260828-pre-opencode-migration.md`) · Backups `~/dev/.pre-opencode-migration-backups/20260828-150335/` · MCP-Inventur `bf-infra/mcp/INVENTORY.md` · Lessons `bf-infra/CLAUDE.md` Abschnitt „Lessons for Sessions" · `docs/handoff-20260828-mcp-bestand-und-performance.md` (MCP-Bestand-Korrektur + Mac-mini-Restriktion).
|
||
|
||
---
|
||
|
||
### TP36 — SSoT via Nautobot für `teilprojekte.md`-Live-Daten + Datei-Aufteilung ▸ 🔜 ANGELEGT 2026-08-28, Scope-Ausarbeitung eigene Session
|
||
- **Auslöser:** Skill-Run 2026-08-28 (`docs/handoff-20260828-session-a-projektstatus.md`) lieferte „Nächster Schritt (2026-08-26): TP31 Schritt 5" als Output, obwohl seit dem 26. drei Folge-Sessions parallel offen waren. **Manuell gepflegte Doku mit Live-Daten verliert gegen die Realität, sobald sie lang wird.**
|
||
- **Befund:** Drei Stellen pro Edit berücksichtigen müssen — Quick-Index (Tabelle), TP-Abschnitt, Ist-Stand-Block. Keine schlägt die andere. Wer eine ändert, **muss** die anderen mitlesen. Die Datei hat aktuell 1940 Zeilen, der Ist-Stand-Block steht bei Z. 28, das Archiv-Cover bei Z. 1771. Strukturelles Risiko, kein Einzelfall.
|
||
- **Scope (Skizze, eigene Session):**
|
||
1. **Nautobot Custom Fields für Live-Daten** — RouterOS-Version, Compliance-Zahl (104/104, 26 Rules × 4 Geräte), DHCP-Server-Anzahl, MGMT-IPs, DNS-Upstream-Modus. Script `nautobot/gc-snapshot.py` rendert `docs/teilprojekte.md` (oder einen schmaleren Ersatz) als **read-only Snapshot**.
|
||
2. **Datei-Aufteilung** — `docs/teilprojekte-index.md` (1 Seite: nur aktive TPs, Titel + Status + Datum), `docs/teilprojekte-aktuell.md` (Live-Snapshot aus Nautobot), `docs/teilprojekte-archiv.md` (bleibt Volltext). `/projektstatus`-Skill liest Index + Archiv, nicht die Snapshot-Datei.
|
||
3. **Archiv-Anzeige im Quick-Index** — die heute 34 Namen stehen heute in *einer* Zeile am Ende. Sichtbarkeit = „Geschichte zählt". Quick-Index-Block bekommt eine zweite Tabelle mit „abgeschlossen seit …" und Link aufs Archiv.
|
||
4. **Migrationspfad** — kompatibel mit bestehendem Workflow (`/projektstatus`-Skill konsumieren, niemand außer mir editiert Live-Markdown). Testphase = beide Dateien parallel führen.
|
||
- **Abgrenzung:** TP36 ist Querschnitt, nicht Kern-BF. Eigene Session, eigener Handoff. Nicht heute.
|
||
- **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**
|
||
|
||
*(Nummer 2026-08-31 von 37 auf 38 korrigiert und von Christoph bestätigt: TP37 war zeitgleich in
|
||
einem zweiten Branch für „Nautobot als Netzwerkdoku" vergeben worden.)*
|
||
|
||
> **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)
|
||
> **Christophs Formulierung (2026-08-30):** *„können wir das setup mit den cf-connectoren dann auch in
|
||
> nautobot abbilden. quasi eine durchgehende netzwerkdoku stück für stück. muss eh fast sein, da du auf
|
||
> den mikrotiks ja nur via gc/nautobot deployst."*
|
||
|
||
- **Auslöser:** der TP16.3-Schnitt am 2026-08-30. Um ihn zu planen, mussten **sechs `cloudflared`-Container
|
||
über drei Hosts hinweg per SSH und Portainer-API zusammengesucht** werden (LXC400 `.126`, macmini `.167`,
|
||
NAS `.10`), und die Zuordnung Tunnel→Connector→Host existierte **nirgends als Datensatz** — nur verstreut
|
||
in Prosa. Dieselbe Suche fällt bei jedem weiteren Eingriff erneut an.
|
||
- 🔴 **Befund (live gegen die API gelesen 2026-08-30) — das Fundament fehlt, nicht nur die Connector-Zeilen:**
|
||
```
|
||
Devices: 8 (ap1, ap2, crs326-core, crs326-branch + cudy1/cudy2/sw1/sw2 decommissioned)
|
||
Apps: golden-config Platform: 1 (RouterOS)
|
||
ipam.Service: 0 · CustomField: 0 · Relationship: 0 · VirtualMachine: 0 · Tag: 0
|
||
```
|
||
Nautobot kennt heute **ausschließlich die vier MikroTik**. Kein Host, kein Container, kein Dienst ist
|
||
erfasst — weder Caddy (`caddy-bf1`), noch AdGuard (`agh-bf1`/`bf2`), noch pve/NAS/macmini, noch Nautobot
|
||
selbst. ⇒ **TP37 ist zuerst eine Datenmodell-Frage, erst danach eine Connector-Frage.**
|
||
- **Scope (Vorschlag, Reihenfolge ist der Punkt):**
|
||
1. **Substrat abbilden** — pve/NAS/macmini als Hosts, die LXCs und Docker-Container als
|
||
`virtualization.VirtualMachine` (Cluster je Host). Damit existiert erstmals ein Ort, an dem
|
||
„wo läuft was" steht.
|
||
2. **Dienste abbilden** — `ipam.Service` je exponiertem Port (Caddy `:80/:443`, AdGuard `:53`,
|
||
Nautobot `:8080`, n8n `:5678`, …). Das ist Nautobot-Kernmodell, **keine eigene App nötig**.
|
||
3. **CF-Connectoren als erster Anwendungsfall** — Tunnel (`bf-public`, historisch `bf-mm`/`bf-nas`/`bf-pve`)
|
||
als Objekt, Connector als Relation Tunnel↔Host, Ingress-Regel als Hostname→Backend-Paar.
|
||
Modellwahl offen: Custom Fields + Relationships auf Bestandsmodellen **oder** eine kleine eigene App.
|
||
🔴 **Erst Punkt 1–2 entscheiden, dann diese Frage** — umgekehrt entsteht ein Modell für einen Sonderfall.
|
||
4. **Drift-Check** — Skript liest CF-API (`cfd_tunnel` + `configurations`) und vergleicht gegen Nautobot,
|
||
analog `nautobot/gc-configplan.py --refresh-compliance`. **Read-only zuerst**; ob Nautobot je nach
|
||
Cloudflare *schreibt*, ist eine spätere, eigene Entscheidung.
|
||
- **Abgrenzung (wichtig, sonst überlappt es mit zwei offenen TPs):**
|
||
- **TP34** (Caddyfile via GC) rendert *Config aus Daten*. TP37 liefert **die Daten**, auf denen TP34
|
||
aufsetzt — Punkt 1+2 sind TP34s fehlende Voraussetzung. **TP37 vor TP34.**
|
||
- **TP36** (SSoT für `teilprojekte.md`) rendert *Doku aus Daten* und braucht dieselben Custom Fields.
|
||
Läuft parallel, gleiches Fundament.
|
||
- **Nicht** in TP37: Golden Config für Nicht-Netzgeräte (Caddy/AdGuard deployen) — das ist TP34.
|
||
**Nicht** in TP37: Cloudflare-Writes aus Nautobot heraus.
|
||
- **Abh.:** TP21/TP22 ✅ (Nautobot + GC-Pipeline stehen) · TP32 ✅ (Nummern-Systematik liefert die
|
||
Klassifikation für Punkt 1) · TP16.3 (liefert den Anwendungsfall und den Beleg, warum es fehlt).
|
||
Hängt an **nichts Offenem**.
|
||
- ⚠️ **Ehrliche Einschränkung:** Nautobot ist bis heute bewusst schlank (8 Devices, 0 Custom Fields) —
|
||
die Pipeline ist stabil und **104/104 compliant**. TP37 vergrößert die Angriffsfläche dieser Pipeline.
|
||
Deshalb Punkt 1–2 als eigener, abnehmbarer Schritt **mit Compliance-Gegenprobe danach**, nicht als
|
||
Nebeneffekt von Punkt 3.
|
||
- **Status:** 🔜 angelegt 2026-08-30, Scope-Ausarbeitung in eigener Session. Kein Runbook.
|
||
|
||
---
|
||
|
||
## Abgeschlossene Teilprojekte ▸ [`teilprojekte-archiv.md`](teilprojekte-archiv.md)
|
||
|
||
**34 abgeschlossene TPs** liegen dort im Volltext, unverändert und mit eigenem Inhaltsverzeichnis.
|
||
📊 **Für einen Statusbaum reicht diese Datei:** die Tabelle unten nennt **jedes** archivierte TP, und
|
||
die Zeile „✅ Durch (Fundament)" weiter unten führt sie noch einmal mit Abschlussdatum. Das Archiv muss
|
||
dafür **nicht** geöffnet werden — es trägt die Begründungen, nicht den Status.
|
||
|
||
Kurzorientierung, wo was zu finden ist:
|
||
|
||
| Thema | TPs im Archiv |
|
||
|---|---|
|
||
| **Fundament / Fabric** | [TP1](teilprojekte-archiv.md#tp1) ALLNET-Core *(Gerät ausgebaut)* · [TP2](teilprojekte-archiv.md#tp2)/[TP2.1](teilprojekte-archiv.md#tp2-1) Omada *(ausgebaut)* · [TP15](teilprojekte-archiv.md#tp15) HW-Refresh CRS326 · [TP21.1](teilprojekte-archiv.md#tp21-1) Core-Cutover-Recovery · [TP4](teilprojekte-archiv.md#tp4) Router-Cutover *(Rolle seit 08-08 auf dem Core)* |
|
||
| **WLAN** | [TP3](teilprojekte-archiv.md#tp3) *(historisch)* · [TP3.1](teilprojekte-archiv.md#tp3-1) · [TP3.2](teilprojekte-archiv.md#tp3-2) · [TP3.3](teilprojekte-archiv.md#tp3-3) *(in TP25 aufgegangen)* · [TP28](teilprojekte-archiv.md#tp28) CAPsMAN auf dem Core · [TP25-Langfassung](teilprojekte-archiv.md#tp25-lang) |
|
||
| **Firewall / QoS** | [TP5](teilprojekte-archiv.md#tp5) deny-by-default · [TP26](teilprojekte-archiv.md#tp26) `ip-firewall-filter` unter Drift-Kontrolle · [TP6](teilprojekte-archiv.md#tp6) CAKE *(geschlossen statt gebaut)* · [TP6.1](teilprojekte-archiv.md#tp6-1) Upload-Priorisierung |
|
||
| **Discovery / Media** | [TP7](teilprojekte-archiv.md#tp7) mDNS · [TP7.1](teilprojekte-archiv.md#tp7-1) SSDP-Relay · [TP7.2](teilprojekte-archiv.md#tp7-2) HA↔Sonos |
|
||
| **Hosts / Migration** | [TP14](teilprojekte-archiv.md#tp14) (+[.1](teilprojekte-archiv.md#tp14-1)/[.2](teilprojekte-archiv.md#tp14-2)/[.2a](teilprojekte-archiv.md#tp14-2a)/[.3](teilprojekte-archiv.md#tp14-3)/[.5](teilprojekte-archiv.md#tp14-5)) VLAN20-Cutover · [TP17](teilprojekte-archiv.md#tp17) Shelly→VLAN30 · [TP19](teilprojekte-archiv.md#tp19) VLAN1-Retirement |
|
||
| **Control-Plane** | [TP20](teilprojekte-archiv.md#tp20) Platform-Postgres · [TP21](teilprojekte-archiv.md#tp21) Nautobot GC · [TP22](teilprojekte-archiv.md#tp22) Adoption + Compliance-Push · [TP27](teilprojekte-archiv.md#tp27) RouterBOOT 7.24 · [TP-R](teilprojekte-archiv.md#tp-r) Capabilities-Sweep |
|
||
| **Zugang** | [TP10](teilprojekte-archiv.md#tp10) Multi-Site-VPN *(offen nur der DDNS-Updater, s. Follow-ups)* · [TP16.1](teilprojekte-archiv.md#tp16-1) natives CF-Mesh |
|
||
| **Verworfen** | [TP9](teilprojekte-archiv.md#tp9) Gateway-HA (User-Entscheid 2026-08-08) |
|
||
|
||
## Reihenfolge / Abhängigkeiten
|
||
|
||
### 🎯 Kritischer Pfad (Stand 2026-08-23) — **LEER**
|
||
|
||
```
|
||
TP5 E1 ✅ ─► E2 ✅ ─► E2-Nachtrag ✅ ─► E3 Drop ✅ ─► E4 input+MGMT ✅ ─► E5 MAIN→IOT ✅ ─► TP6 ✅ GESCHLOSSEN
|
||
|
||
🎯 DER KRITISCHE PFAD IST LEER (2026-08-23).
|
||
Das Projekt ist in seinem Kern fertig: deny-by-default steht, Golden Config
|
||
steht, WLAN steht, VLAN1 ist weg, und TP6 ist beantwortet statt offengelassen.
|
||
Was jetzt kommt, entscheidet Christoph — nichts blockiert mehr etwas.
|
||
```
|
||
> ➡️ **Update 2026-08-22 (Sitzungsende):** TP25 hat den Pfad seit dem 08-20 blockiert und ist im
|
||
> Kern **erledigt** — die Terrasse ist repariert, der Dead Spot weg, Fund E abgestuft. Damit ist
|
||
> **TP6 wieder der einzige Punkt auf dem kritischen Pfad**, und seine Prämisse ist inzwischen
|
||
> gemessen (`tp6-praemisse-messrunbook.md` §5): **formal Fall B** (+28…47 ms in der Senderichtung),
|
||
> **aber durch sechs Lastphasen mit laufendem Videocall kein einziger Problem-Klick.** Die
|
||
> nächste Sitzung entscheidet daher **bauen oder begründet schließen** — sie misst nicht neu.
|
||
> Übergabe mit den Sitzungszuschnitten: [`handoff-20260822-naechste-schritte.md`](handoff-20260822-naechste-schritte.md).
|
||
|
||
> 🟢 **Update 2026-08-23 (Endstand): TP6 ist beantwortet — und der kritische Pfad damit leer.**
|
||
> Gebaut wurde, wie entschieden. Der erste Anlauf (`/queue tree parent=vlan100`) war wirkungslos,
|
||
> **aber nicht aus dem zuerst notierten Grund**: nicht der Switch-Chip, sondern die
|
||
> MikroTik-dokumentierte Regel, dass Interface-HTB bei hw-offloadetem out-interface nicht greift.
|
||
> Ueber `parent=global` + Mangle-Mark **funktioniert CAKE einwandfrei** (zaehlt 6 MB, drosselt
|
||
> 9,3 → 7,78 Mbit). Und genau deshalb ist das Ergebnis belastbar: **die Latenz unter Last bessert
|
||
> sich trotzdem nicht — bei keiner Bandbreite, auch nicht bei 52 % der Linerate** (42,4 / 43,3 /
|
||
> 44,0 / 44,6 ms). Der Puffer sitzt im **DOCSIS-Uplink**, nicht im Router. ⇒ **TP6 schliessen:
|
||
> nicht mangels Nutzen, sondern weil das Werkzeug nicht an die Ursache reicht.** Details im
|
||
> TP6-Abschnitt oben, Merkregeln in `ref_queue_htb_platzierung_docsis`.
|
||
|
||
> 🔴 **Korrektur wenige Stunden spaeter (2026-08-23, TP6.1): die DOCSIS-Erklaerung oben ist FALSCH.**
|
||
> Der Puffer sitzt nicht unabaenderlich im Uplink — er loest sich auf, sobald der Verkehr in
|
||
> **getrennte Klassen** faellt: NAS-Bulk `priority=8` gegen interaktiv `priority=1` bringt dieselbe
|
||
> Strecke unter derselben Saettigung von **40,8 auf 11,0 ms (−73 %)**. Der Fehler steckte in der
|
||
> **Messmethode** — Last und Messung liefen vom selben Host, ein Shaper kann eine Queue nicht
|
||
> priorisieren, in der nur eine Klasse liegt. ⇒ **TP6 bleibt geschlossen** (CAKE war das falsche
|
||
> Werkzeug), **aber nicht aus dem dort notierten Grund.** Der Rest steht in **TP6.1**
|
||
> ([Archiv](teilprojekte-archiv.md#tp6-1)) und in `ref_queue_htb_platzierung_docsis`.
|
||
|
||
📜 *Die folgende Fassung stammt vom 2026-08-18 und ist **erledigt** — die Praemisse ist am 08-20/21
|
||
gemessen, TP6 am 08-23 geschlossen. Bewusst erhalten, weil sie zeigt, warum nicht gebaut, sondern
|
||
zuerst gemessen wurde:*
|
||
|
||
**TP5 ist am 2026-08-18 abgeschlossen.** Damit steht rechnerisch nur noch **TP6** auf dem kritischen Pfad —
|
||
🟠 **aber dessen Prämisse ist offen**: eine Messung von 2026-06-16 (`docs/call-quality-diagnosis.md`)
|
||
bescheinigt dem Netz bereits **A/A+** und hält CAKE für „marginal". Der nächste Schritt ist deshalb ein
|
||
**Bufferbloat-Test gegen den heutigen Edge**, nicht der Bau einer Queue. Fällt er gut aus, ist der
|
||
kritische Pfad **leer** und das Projekt in seinem Kern fertig — dann entscheidet Christoph, was das neue
|
||
Ziel ist. Alles andere läuft nebenher und blockiert nichts.
|
||
|
||
1. ✅ **TP5 Etappe 1–4 — komplett durch** (2026-08-16 bis 2026-08-18). Details im TP5-Abschnitt oben.
|
||
Die Wartezeit-Position, die hier bis 2026-08-17 als eigener Schritt stand, ist eingeloest: statt
|
||
eines mehrtaegigen Fensters wurde der Nachtrag aus einem **dreieinhalbstuendigen** Fenster
|
||
abgeleitet und der Drop mit Auto-Revert plus weiterlaufender Log-Regel abgesichert.
|
||
Die Vorbedingung D2 (iPhone `.30.201`) ist **gegenstandslos**: das Geraet hing dort bewusst zum
|
||
Anlernen eines IoT-Geraets (User 2026-08-17). Wer im IOT-SSID sitzt, ist im selben VLAN wie das
|
||
Geraet und beruehrt die `forward`-Kette gar nicht; fuer Ad-hoc-Zugriffe aus MAIN gibt es jetzt die
|
||
selbstverfallende Gruppe `jit-admin`.
|
||
2. ✅ **TP6 — QoS/CAKE ▸ GESCHLOSSEN 2026-08-23.** Gebaut, gemessen, wieder ausgebaut. CAKE ist
|
||
auf diesem Geraet baubar (Global-HTB + Mangle) und wirkt technisch — verbessert die Latenz
|
||
unter Last aber bei **keiner** Bandbreite, auch nicht bei 52 % der Linerate. Der Puffer sitzt
|
||
im DOCSIS-Uplink, nicht im Router *(⚠️ **widerlegt von TP6.1 am selben Tag** — s. Korrektur oben)*.
|
||
Queue und Template-Block sind entfernt, die ComplianceRule
|
||
`queue` bleibt und bewacht den Abschnitt als leer. **Damit ist der kritische Pfad leer.**
|
||
💡 Aus dem Abschluss ist **TP6.1** entstanden (Saettigung vermeiden statt Bufferbloat
|
||
bekaempfen) — Christophs Gedanke, den NAS-Sync zurueckzuhalten. ~~Steht als Vorschlag, nicht
|
||
auf dem Pfad.~~ **✅ Am selben Tag gebaut und gemessen: 40,8 → 11,0 ms.** ~~Das eigentliche Projektziel; **keine offenen Blocker mehr**.~~
|
||
**Korrigiert 2026-08-19:** kein *Blocker*, aber eine offene **Prämisse** — der Satz oben und dieser
|
||
Punkt widersprachen sich seit dem 18. Richtig ist: der nächste Schritt ist eine **Messung**, kein Bau.
|
||
Runbook liegt bereit: `docs/tp6-praemisse-messrunbook.md`. Fällt sie A/A+ aus, wird TP6 **geschlossen
|
||
statt gebaut** — dann ist der kritische Pfad leer und das Projekt in seinem Kern fertig.
|
||
|
||
**Eine harte Reihenfolge-Regel dazwischen:** TP5 darf die **DNS-Bypass-Sperre erst schließen, wenn TP23s
|
||
Ausnahme-Oberfläche steht** — der bisherige Workaround „Client von Hand auf `1.1.1.1`" *ist* genau dieser
|
||
Bypass. Alles andere aus TP5 ist davon unabhängig und kann vorher laufen.
|
||
|
||
⚠️ **Und eine zweite, aus TP16.1/TP16.3:** der Drop braucht `cf-mesh` explizit — steht seit Etappe 2 als
|
||
`TP5-E2: mesh->LAN` / `LAN->mesh` drin. Ohne diese zwei Zeilen sperrt Etappe 3 den Fernzugang aus, also genau
|
||
den Weg, über den man den Fehler zurücknehmen wollte.
|
||
|
||
### Follow-ups nebenher (blockieren nichts)
|
||
|
||
- **TP25 — WLAN-Stabilisierung (NEU 2026-08-17):** Audit durch, **6 Funde** (2,4 GHz fährt 40 MHz statt der
|
||
profilierten 20; ap2 sendet auf 5 GHz mit **9 statt 18 dBm**, weil ch149 unter ETSI leistungsbegrenzt ist;
|
||
80 MHz kostet Randabdeckung; unzulässiger Channel-Override auf `w2-iot`; keine `access-list`; 5-Minuten-Takt
|
||
bei IoT-Trennungen offen). Umsetzung in 6 Schritten, alles via GC. **Übernimmt den offenen Rest von TP3.3.**
|
||
- **TP16.3 — cloudflared-Reduktion (NEU 2026-08-17):** Recherche durch. Der Mesh **ist** schon der private
|
||
Datenpfad (im TP5-Log mit 137 Zeilen belegt); Container braucht nur noch der **öffentliche** Ingress.
|
||
Vorschlag: `nautobot` als Probe aus dem CF-Ingress nehmen.
|
||
- ~~**DoH-Cloudflare auf `crs326-core`**~~ ✅ **erledigt 2026-08-18** (TP23 Stufe 1 = TP21.1 TODO 1).
|
||
- ~~**TP6.1 — Saettigung vermeiden**~~ ✅ **LIVE 2026-08-23: 40,8 → 11,0 ms unter Last** (−73 %).
|
||
NAS-Sync weicht bei Last zurueck, ComplianceRule `ip-firewall-mangle` angelegt, 104/104.
|
||
- **TP24 — DC-USV** (seit 2026-08-16 eigener TP, aus TP15 herausgelöst).
|
||
- ~~**TP19 — VLAN1-Client-Retirement / cudy-Teardown**~~ ✅ **geschlossen 2026-08-18** (VLAN1-ARP = 0). Die zwei Reste sind dorthin verschoben, wo sie hingehören: tote CF-Tunnel → TP16, HA-Portainer `setup_retry` → TP11.
|
||
- ~~**TP3.3 — WLAN-Coverage-Kur**~~ → **in TP25 aufgegangen** (2026-08-17). 802.11k/v ist repariert (08-13);
|
||
Re-Walk + Dead-Spot-Kartierung laufen als Schritt 5 von TP25 — und zwar erst **nach** den Funkparameter-Fixes,
|
||
weil vorher gemessene Werte wertlos sind.
|
||
- **TP10 —** DDNS-Auto-Updater für `bf4.emsenhuber.net` (auf dem Core möglich, `device-mode` erlaubt scheduler).
|
||
- 🔑 **Alle Passwort-/Token-Rotationen laufen seit 2026-08-24 unter [TP30](#tp30) — Sammel-TP,
|
||
gebündelt gegen Projektende (User-Entscheid).** Dort steht die Rotationsliste (MikroTik `admin`,
|
||
IoT-PSK, Shelly-Gerätepasswort, CF-Access-Token), der Radius der fünf Konsumenten, der
|
||
Etappe-1-Abschluss und die Fallenliste. **Etappe 1 ist durch**, Etappe 2 wartet bewusst.
|
||
- ~~**Doku-Aufräumen** (`docs/doku-cleanup-session.md`)~~ ✅ **abgeschlossen 2026-08-23.** Punkte A–D
|
||
(08-16), E (08-18/19) und **F — der Split** (08-23) sind durch: abgeschlossene TPs liegen jetzt in
|
||
[`teilprojekte-archiv.md`](teilprojekte-archiv.md), TP25 ist von 394 auf ~100 Zeilen kondensiert,
|
||
diese Datei ist von **2217 auf ~760 Zeilen**. Die Regel dahinter bleibt gültig:
|
||
⚠️ **während einer Doku-Session `teilprojekte.md` nicht parallel bearbeiten** (in der Nacht auf den
|
||
18. viermal committet, das kollidiert sonst) — und **ein TP wandert erst dann ins Archiv, wenn
|
||
nichts mehr daran zu tun ist.** *(Ergänzt 2026-08-30: und **zurück**, wenn doch wieder etwas
|
||
zu tun ist — das Archiv ist eine Ablage, kein Endlager.)*
|
||
|
||
### Nachgelagert / research-getrieben
|
||
|
||
**TP8** (Identity/802.1X) · **TP11** (Monitoring) · **TP12** (IPv6) · **TP13** (Botschafts-WLANs) ·
|
||
**TP16.2** (Härtung bypass-Dienste) · **TP18** (Garage-Objektspeicher) · **TP20.1** (Vaultwarden → Platform-PG) ·
|
||
**TP23** (DNS-Policy, s. Regel oben) · **TP30** (Credential-Rotation — nicht research-, sondern
|
||
**termin**-gated: gebündelt gegen Projektende, User-Entscheid 2026-08-24).
|
||
*(**TP17.1** — Shelly Gen4 → Thread, aktiviert 2026-08-23 — läuft **nicht hier**, sondern im
|
||
HA-Projekt `~/dev/ha-xstate`; netzseitig ist nichts zu bauen. Es blockt bewusst die PSK-Rotation.)*
|
||
*(**TP16.1** stand hier bis 2026-08-17 fälschlich als offen — es ist seit demselben Tag ✅ abgeschlossen und
|
||
steht unten unter „Durch" (Volltext im [Archiv](teilprojekte-archiv.md#tp16-1)). Nachfolger ist
|
||
**TP16.3**, das oben unter Follow-ups läuft.)*
|
||
|
||
### ✅ Durch (Fundament — nicht mehr planen, nur nachschlagen)
|
||
|
||
> 📚 **Alle folgenden TPs stehen seit 2026-08-23 im [Archiv](teilprojekte-archiv.md)** — im Volltext,
|
||
> mit Inhaltsverzeichnis. Diese Zeile bleibt als *Landkarte*: sie sagt, was fertig ist; das Archiv
|
||
> sagt, wie es dazu kam.
|
||
|
||
TP1 · TP2 · TP2.1 · TP3 (Rebuild 2026-08-08) · TP3.1 · TP4 (Rolle seit 08-08 auf dem Core) · TP7 · TP7.2 ·
|
||
TP10 (Kern) · TP14 (+14.1/14.2/14.2a/14.3/14.5) · TP15 · TP16 (Kern) · TP17 · TP19.1 · TP20 · TP21 · TP21.1 ·
|
||
TP22 · TP-R · **TP3.2** (08-17) · **TP5** (08-18) · **TP7.1** (08-18) · **TP19** (08-18) · **TP23 Stufe 1** (08-18) · **TP6** (08-23, geschlossen statt gebaut) · **TP6.1** (08-23, Upload-Prio live) · **TP26** (08-17) · **TP27** (08-17/18) · **TP28** (08-17, GC-Modell 08-18). **Verworfen:** TP9 (Gateway-HA, User-Entscheid 2026-08-08). **Obsolet:** Omada-PVID-Fix
|
||
(User-Entscheid 2026-07-22 — „nie wieder aufgreifen").
|
||
|
||
<details><summary>📜 Historische Fassung dieses Abschnitts (bis 2026-08-14 fortgeschrieben, hier bewusst erhalten)</summary>
|
||
|
||
> 🎯 **AKTUELLER KRITISCHER PFAD (Stand 2026-07-16, überschreibt die Reihenfolge unten): TP20 (Platform-Postgres) → TP21 (Nautobot) — Ziel: fertig vor CRS326-Produktivsetzung, spätestens Montag 2026-07-20.** TP18 (Garage-S3) und die Komodo-Abhängigkeit wurden am selben Tag als Blocker entfernt/entkoppelt (s. TP20), um diese Deadline zu halten — beide werden später nachgezogen, nicht mehr vorher abgewartet. **➡️ Stand-Update 2026-08-08:** TP20/TP21-Plattform steht, Core-Cutover (TP21.1) + WLAN-Rebuild (TP3) + VLAN20-Migration (TP14) sind durch. Neuer Rest-Pfad: **TP22 (Device-Adoption + Compliance-Push) → TP5 (Deny-by-default) → TP6 (CAKE, das Ziel).** Kleine Follow-ups davor: DoH-Cloudflare auf core, USV (**TP24**). **➡️ Stand-Update 2026-08-13 (Abschluss): TP22 ist DURCH** (alle 5 Schritte; Fleet adoptiert, Deploy-Pipeline scharf, Datenmodell as-built, Secret-Handling entschieden) → **kritischer Pfad jetzt: TP5 (Deny-by-default) → TP6 (CAKE)**. Kleine Follow-ups davor: DoH-Cloudflare auf core, DC-USV (**TP24**), **Doku-Aufräumen (`docs/doku-cleanup-session.md`, vorbereitet 2026-08-14 — dieses Dokument beschreibt an mehreren Stellen noch die Topologie vor dem Core-Cutover)**. ➡️ **Neu 2026-08-14: TP23 (DNS-Policy & Self-Service) hängt an TP5** — und zwar in *beide* Richtungen: TP23 braucht TP5s Firewall, **TP5 darf aber die DNS-Bypass-Sperre erst schließen, wenn TP23s Ausnahme-Oberfläche steht** (der bisherige Workaround „Client auf 1.1.1.1" *ist* dieser Bypass). Ebenfalls in TP5 gezogen: **TP7.1** (SSDP-Reflector für die macOS-Sonos-App). ~~Kabel `core ether4`→`ether2` (Sideboard/TV)~~ **entfällt (2026-08-14):** gelöst wurde am **Port**, nicht am Kabel (User-Vorgabe „Patchfeld-Nummerierung wird nicht wegen einer Config geändert") — `ether4` steht seit 08-13 auf untagged VLAN40, End-to-End-Beweis am 08-14 erbracht, s. **TP21.1 Punkt 4**. Historisch: **TP22 war das nächste Arbeitspaket** — TP17 (Shelly-Migration) ist parallel dazu vollständig abgeschlossen (alle Shellys auf VLAN30, User-bestätigt). Grund für den Nautobot-First-Ansatz: Governance-Entscheid „Config exklusiv über Nautobot" (s. oben) macht Nautobot zur Voraussetzung für praktisch jede weitere Router-/Switch-Änderung. Die Punkte 3-4 unten (Omada-PVID-Fix etc.) sind **obsolet** (User-Entscheid 2026-07-22 — Omada spielt seit TP15-Korrektur keine Rolle mehr, nie wieder aufgreifen) — ganze Liste braucht mal eine Aufräum-Session, hier nur der aktuelle Fokus vorangestellt.
|
||
> ✅ **TP16 (CF-Tunnel Round-Robin/macvlan-Host-Isolation → reziprokes 3-Tunnel-HA)** KERN UMGESETZT 2026-07-10 (#1033 strukturell weg). Offen nur Access-Strang (WARP/`vw`) = user-gated. As-Built `docs/cf-tunnel-mapping.md §10`.
|
||
1. ~~**TP1 (GE8) → TP2 (Omada Controller).**~~ ✅ erledigt 2026-06-15.
|
||
2. ~~**TP4 (Router-Cutover L3-Infra + WAN-Edge).**~~ ✅ erledigt 2026-06-15 (L3) + 2026-07-01 (Edge-Cutover, FB physisch weg).
|
||
3. **Jetzt: Omada-PVID-Fix** — Omada Access-Profile korrigieren (PVID 10/20, untagNetworkIds bereinigen, sicher P3/P5/P6 zuerst), dann IP-Migration NAS/macmini/pve auf VLAN-Subnetze + DNS-Einträge updaten. **IP-Migration NAS/pve = TP14** (NAS ✅ 2026-07-10, pve 🔜 Session B).
|
||
4. **Dann:** TP5 (Deny-by-default fertigziehen — Sonos-/HA-/mDNS-Holes + WAN-Input-Seal stehen bereits; **TP5.1 = DNS-Redirect + Live-Torch für Low-Trust-Geräte/Airfryer hängt dran**) → TP6 (QoS/CAKE).
|
||
~~TP7 (mDNS)~~ ✅ 2026-07-02 (inkl. HA/vlan20), ~~TP3 (WLAN/CAPsMAN)~~ ✅ 2026-06-18, ~~TP3.1 (ap2-VLAN-Trunk)~~ ✅ 2026-07-01,
|
||
~~TP3.2 (P750W/Discovery)~~ ✅ 2026-07-02, ~~Sonos-Migration~~ ✅ 2026-07-02 (alle 10 auf VLAN 40).
|
||
⏳ **TP3.3 (WLAN-Coverage-Kur) offen** — Fortsetzung neue Session (`docs/wifi-p750w-coverage-and-map.md`).
|
||
5. ~~**TP10 (VPN)**~~ ✅ Kern durch (2026-06-27) + Edge-Cutover (2026-07-01) → DURCH. Offen nur: DDNS-Auto-Updater.
|
||
6. **Nachgelagert/Research-getrieben:** TP8 (Access), TP9 (HA), TP11 (Monitoring + CF-Tunnel-Track-B/C), TP12 (IPv6), TP13 (Spaß).
|
||
7. **Foundation (2026-07-14):** **TP15** (MikroTik-Switching / Omada-WLAN, 2× CRS326 bestellt) ∥ **TP20 (Platform-Postgres, single-site replication-ready)** → **TP21 (Nautobot Golden Config)** ∥ **TP20.1 (Vaultwarden→PG + 2-Site-HA)**. TP20 hängt an **TP18 ①** (Garage-Bucket für pgBackRest).
|
||
|
||
</details>
|