Träger für TP45 Schicht C4. TP34 (Caddyfile-Rendering) bleibt verworfen (#24); der Daten→Config-Gedanke lebt als TP34.2 weiter. Prosa-Abschnitt + Anker angelegt, Schicht-C-Tabelle / „Offen in C" / Abh. / Follow-up / TP37-Abgrenzung auf TP34.2 umgestellt, Archiv-Zeiger ergänzt. Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
1983 lines
270 KiB
Markdown
1983 lines
270 KiB
Markdown
# BF-Netzwerk — Teilprojekte-Archiv (abgeschlossene TPs)
|
||
|
||
> **Was hier steht:** jedes Teilprojekt, an dem **keine offene Arbeit** mehr hängt — im **Volltext
|
||
> und unverändert**, so wie es in `teilprojekte.md` stand. Nichts ist gekürzt oder umgeschrieben
|
||
> worden; der Split am **2026-08-23** hat Text nur verschoben.
|
||
>
|
||
> **Was hier NICHT steht:** der aktuelle Stand. Der lebt in **[`teilprojekte.md`](teilprojekte.md)** —
|
||
> Ist-Stand, kritischer Pfad und alle **offenen** TPs. Eine frische Session braucht **nur** jene
|
||
> Datei; diese hier liest man, wenn man wissen will, **warum** etwas so entschieden wurde.
|
||
>
|
||
> ⚠️ **Nicht als Ist-Stand lesen.** Jeder Abschnitt beschreibt den Stand zum Zeitpunkt seines
|
||
> Abschlusses. Wo ein Gerät seither ausgebaut oder eine Rolle umgezogen ist, steht das als datierter
|
||
> Nachtrag drin (Projektkonvention: **fortschreiben, nicht überschreiben**) — aber die Wahrheit über
|
||
> das heutige Netz ist das Gerät, dann `CLAUDE.md`, dann `teilprojekte.md`.
|
||
|
||
**Warum überhaupt getrennt (User-Anstoß 2026-08-23: „die teilprojekte.md beginnt zu explodieren"):**
|
||
die Datei war auf **2217 Zeilen** gewachsen, **49 % davon abgeschlossene TPs**. Das Wachstum kam nicht
|
||
aus der *Zahl* der TPs, sondern aus der **Historie erledigter** — und die wird gelesen, wenn jemand
|
||
*gezielt* danach sucht, nicht beim Einstieg. Bewusst **nicht** getrennt wurde nach TP (das wären ~35
|
||
Dateien): die Querbezüge, die das Dokument wertvoll machen — Reihenfolge, Abhängigkeiten, „TP23 hängt
|
||
an TP5 in *beide* Richtungen" — leben von der Nähe im selben Text. Ein Schnitt pro TP hätte die
|
||
teuerste Eigenschaft der Datei zerstört, um ihr billigstes Problem zu lösen. Begründung im Detail:
|
||
[`doku-cleanup-session.md`](doku-cleanup-session.md) Punkt F.
|
||
|
||
---
|
||
|
||
## Inhalt
|
||
|
||
| TP | Thema | Status bei Abschluss |
|
||
|---|---|---|
|
||
| [TP1](#tp1) | ALLNET Core-Switch finalisieren | ✅ FERTIG (2026-06-15) → ❌ Gerät AUSSER BETRIEB (2026-08-08, durch crs326-core ersetzt) |
|
||
| [TP2](#tp2) | Omada Access-Switch + Controller | ✅ FERTIG (2026-06-15) |
|
||
| [TP2.1](#tp2-1) | Omada-Switch (sw2/ES208G) → Standalone-Recovery (kein Controller) | ✅ FERTIG (2026-07-16, User solo) |
|
||
| [TP3](#tp3) | WLAN | 📜 VOLLSTÄNDIG HISTORISCH |
|
||
| [TP3.1](#tp3-1) | ap2 VLAN-Trunk-Fix | ✅ Config live (2026-07-01) |
|
||
| [TP3.2](#tp3-2) | WLAN Coverage / P750W-Discovery | ✅ ABGESCHLOSSEN (2026-08-17, User-Test erfolgreich) |
|
||
| [TP3.3](#tp3-3) | WLAN Coverage-Kur | 📜 IN TP25 AUFGEGANGEN (2026-08-17) |
|
||
| [TP4](#tp4) | Router-Cutover (hAP#1 = Router + WAN-Edge) | ✅ FERTIG (2026-07-01) |
|
||
| [TP5](#tp5) | Firewall & Inter-VLAN-Policy | ✅ DURCH (2026-08-18): deny-by-default in BEIDEN Ketten scharf · + E5 (2026-08-19) |
|
||
| [TP6](#tp6) | QoS/CAKE + Call-Quality-A/B-Test | ✅ GESCHLOSSEN 2026-08-23 — ⚠️ Begründung darin von TP6.1 überholt |
|
||
| [TP6.1](#tp6-1) | Saettigung vermeiden statt Bufferbloat bekaempfen | ✅ LIVE UND GEMESSEN (2026-08-23): 40,8 → 11,0 ms unter Last |
|
||
| [TP7](#tp7) | Media-Discovery (mDNS) für VLAN 40 | ✅ FERTIG (2026-07-02) |
|
||
| [TP7.1](#tp7-1) | SSDP-Relay (macOS-Sonos-App über VLAN-Grenze) | ✅ LÄUFT (2026-08-18) |
|
||
| [TP7.2](#tp7-2) | HA↔Sonos static-host binding + VLAN1/GUEST-Discovery | ✅ FERTIG (2026-07-03) |
|
||
| [TP9](#tp9) | Gateway-/Router-HA | ❌ OBSOLET (User-Entscheid 2026-08-08) |
|
||
| [TP10](#tp10) | Multi-Site VPN | ✅ KERN DURCH (2026-06-27) |
|
||
| [TP14](#tp14) | Host/Container-IP-Migration VLAN1→VLAN20 (Docker-Cutover NAS+pve) | ✅ DURCH (2026-07-13; live re-verifiziert 2026-08-08) |
|
||
| [TP14.1](#tp14-1) | Session A: NAS fertigstellen | ✅ DURCH (2026-07-10); Omada-Inform/sw2 VERTAGT |
|
||
| [TP14.2](#tp14-2) | Session B: pve-Docker-LXC (.126) → VLAN20 | ✅ DURCH (2026-07-10) (ipvlan) |
|
||
| [TP14.2a](#tp14-2a) | pve-mcp Single-Session-Fix + VLAN20-Host-DNS | ✅ (2026-07-10) |
|
||
| [TP14.3](#tp14-3) | Session C: Dormante Stacks vlan20-tauglich + Referenz-Sweep | ✅ Referenz-Sweep durch (2026-07-16); dormante Stacks weiter bewusst nicht deployt |
|
||
| [TP14.5](#tp14-5) | Session E: Freigabe-Gate → TP5-Firewall | ✅ ERFÜLLT (2026-08-08) |
|
||
| [TP15](#tp15) | HW-Refresh: Switching + Router | ✅ ABGESCHLOSSEN (2026-08-08) |
|
||
| [TP16.1](#tp16-1) | Private Access via native Cloudflare Mesh (WireGuard) auf `crs326-core` | ✅ ABGESCHLOSSEN (2026-08-17) — privater Zugang via nativem WireGuard `cf-mesh` |
|
||
| [TP17](#tp17) | Shelly-Übersiedelung VLAN1 → VLAN30 (IOT) + `shelly-mcp` | ✅ KOMPLETT (User-Bestätigt 2026-08-13): alle Shellys auf VLAN30 |
|
||
| [TP19](#tp19) | cudy-Teardown + VLAN1-Client-Retirement | ✅ GESCHLOSSEN (2026-08-18) |
|
||
| [TP20](#tp20) | Platform-Datenbank (Postgres Precious-Tier + MariaDB Tier-2) | ✅ Plattform live (2026-07-16) |
|
||
| [TP21](#tp21) | Nautobot Golden Config (config-as-code Control-Plane) | 🟢 Plattform + Datenmodell + Templates live |
|
||
| [TP21.1](#tp21-1) | Core-Cutover-Recovery (ap1 → crs326-core als L3-Router) | ✅ NETZ WIEDER LIVE (2026-08-08) — Aufräum-Todos **alle erledigt** (Stand 2026-08-23) |
|
||
| [TP22](#tp22) | Device-Adoption + Golden-Config Compliance-Push (RouterOS-Fleet) | ✅ DURCH (2026-08-13) — Compliance heute **104/104, 0 Drifts** (live 2026-08-23) |
|
||
| [TP26](#tp26) | `/ip firewall filter` unter Drift-Kontrolle | ✅ DURCH (2026-08-17) |
|
||
| [TP27](#tp27) | RouterBOOT-Firmware auf 7.24 nachziehen | ✅ DURCH (2026-08-17/18) |
|
||
| [TP28](#tp28) | WLAN über CAPsMAN auf `crs326-core` | ↩️ **wieder geöffnet 2026-08-30** (ap3) — Volltext in [teilprojekte.md](teilprojekte.md#tp28) |
|
||
| [TP-R](#tp-r) | Router-Capabilities-Sweep | FERTIG (docs/research/routeros-capabilities.md) |
|
||
| [TP39](#tp39) | LXC-Hygiene pve: Resolver-Altlasten, `networking.service`, DB-Reverse-Lookup | ✅ ABGESCHLOSSEN (2026-09-01) — pve+NAS-Sweep grün |
|
||
| [TP34](#tp34) | Caddyfile via Nautobot/GC (generische Reverse-Proxy-Schnittstelle) | ❌ VERWORFEN (2026-09-08) — Caddyfile bleibt manuell · **Fortsetzung: TP34.2 (2026-09-22)** |
|
||
| [TP44](#tp44) | Cloudflare-Estate als Dokumentations-SSoT in Nautobot | ✅ DURCH (2026-09-10) |
|
||
| [TP41](#tp41) | CF-Access-Reparatur + Zero-Trust-Konsolidierung | ✅ ABGESCHLOSSEN (2026-09-10) — Gelis WARP via Android eingerichtet, letzter Blocker weg |
|
||
| [TP43](#tp43) | dw-Standort-DNS-Uplink (site-spezifische Overrides) | ♻️ REOPENED (2026-09-11) — Volltext zurück in teilprojekte.md; interne BF-Namen über WG am dw offen |
|
||
| [TP42](#tp42) | Namens-Vereinheitlichung: ein interner Suffix bf.emsenhuber.net | ✅ DURCH (2026-09-02) — `.bf.lan` raus, `.16` abgebaut, Compliance 140/140 |
|
||
|
||
Dazu am Ende: **[TP25 (Langfassung)](#tp25-lang)** — die Messchronik und die Funde A–E im Volltext.
|
||
TP25 selbst ist **nicht** abgeschlossen und steht weiter in `teilprojekte.md`; nur seine 394 Zeilen
|
||
Herleitung liegen hier.
|
||
|
||
---
|
||
<a id="tp1"></a>
|
||
|
||
### TP1 — ALLNET Core-Switch finalisieren ▸ ✅ FERTIG (2026-06-15) → ❌ Gerät AUSSER BETRIEB (2026-08-08, durch crs326-core ersetzt)
|
||
- ✅ GE8 Trunk (native vlan1, tagged 10/20/30/40/50/90/99/100) — war bereits aktiv.
|
||
- ✅ VLAN 40 umbenannt: UNTRUSTED → **MEDIA** (`switch/cli/04-vlan50-media-untrusted.cli`).
|
||
- ✅ VLAN 50 angelegt + benannt: **UNTRUSTED**.
|
||
- ✅ GE1/GE2 (AP-Trunks) um VLAN 50 ergänzt; GE8 auto-includiert (Trunk).
|
||
- Ergebnis: Core trägt alle VLANs (10/20/30/40/50/90/99/100) an P1/P2/P8.
|
||
|
||
<a id="tp2"></a>
|
||
|
||
### TP2 — Omada Access-Switch + Controller ▸ ✅ FERTIG (2026-06-15)
|
||
- ✅ Omada Controller 6.2.10.17 auf DS923 (macvlan `.175`): `omada/docker-compose.yml`, Stack via Portainer endpointId=14. **Wizard abgeschlossen** (configured=true), Creds in `mcp/omada/.env`.
|
||
- ✅ pve-mcp (samik081) in Claude Code eingetragen (`~/.claude/settings.json`, SSE `.173:3000`).
|
||
- ✅ omada-mcp **live** auf **pve-LXC (`.126`, endpointId=2), ipvlan-IP `.176`**, Stack Id=200, URL in `~/.claude/settings.json`.
|
||
- **Transport:** StreamableHTTP `http://10.40.41.176:3000/mcp` (Health `/health` → 200). Settings-Bug (doppelter Eintrag) behoben 2026-06-15.
|
||
- **Auth:** Omada **Open API Client-Credentials**. Creds in `mcp/omada/.env`. Token-Format: `Authorization: AccessToken=<token>` (nicht Bearer). OMADA_TOOL_CATEGORIES=all (alle 84 read-Tools aktiv; write für VLANs/Ports nicht im MCP implementiert → direkte Open API oder Controller-UI).
|
||
- omadac_id=`adbef8fa…`, site_id=`6a2fe68e…`.
|
||
- ✅ **VLANs im Controller:** 1/10/20/30/40/50/90/99/100 — alle angelegt, Switch adopted (switchConsistent=true).
|
||
- ✅ **Port-Plan (ES208G, alle 8 Ports konfiguriert):**
|
||
| Port | Name | Profil | Gerät | Status |
|
||
|------|------|--------|-------|--------|
|
||
| 1 | uplink | Trunk-ALL | → ALLNET GE8 | ✅ korrekt |
|
||
| 2 | ap2 | Trunk-ALL | hAP ax² #2 | ✅ korrekt |
|
||
| 3 | Port3 | Access-MAIN | — (frei) | ⚠️ PVID=1 |
|
||
| 4 | dock/monitor | **Access-MAIN** | Docking-Station | ⚠️ PVID=1 · **Lifeline-Port, nicht anfassen** |
|
||
| 5 | drucker | Access-MAIN | Drucker | ⚠️ PVID=1 |
|
||
| 6 | Port6 | Access-MAIN | — (frei) | ⚠️ PVID=1 |
|
||
| 7 | macmini | Access-INFRA | Mac mini | ⚠️ PVID=1 · **fixe IP 10.40.41.x, nicht anfassen** |
|
||
| 8 | ds923+ | Access-MAIN | NAS (2. Port) | ⚠️ PVID=1 · **nicht anfassen** |
|
||
- ⚠️ **Profile-PVID-Constraint (bis TP4 offen):** NAS, Mac mini und andere Geräte haben fixe IPs im
|
||
Bereich `10.40.41.x` (VLAN-1-Subnetz der FritzBox). Solange diese nicht auf VLAN-10/20-Subnetze migriert
|
||
sind, muss `nativeNetworkId=Default(VLAN 1)` in allen Access-Profilen bleiben — sonst kein DHCP, kein
|
||
Internet für diese Geräte. Fix nach TP4.
|
||
- ⚠️ **Bekannter Fehler `untagNetworkIds` (Analyse 2026-06-15 via Controller-API v2):**
|
||
Access-MAIN hat `untagNetworkIds=[VLAN1, VLAN20, VLAN10]` (3 untagged!) statt `[VLAN10]`.
|
||
Access-INFRA hat `untagNetworkIds=[VLAN20, VLAN1]` statt `[VLAN20]`. Geräte erhalten Frames
|
||
aus mehreren VLANs ungetaggt → Inter-VLAN-Tests verfälscht. Fix erst sinnvoll nach TP4 zusammen
|
||
mit PVID-Korrektur.
|
||
- **Sichere Testports (kein kritisches Gerät):** P3, P5, P6.
|
||
- ✅ Portainer-Ablösung: **Komodo** empfohlen (einzige Option mit nativem MCP `mcp-server-komodo`). Eigener Task.
|
||
- Abh. erfüllt: TP1 (GE8 Trunk ✅).
|
||
- **→ TP4 wird vorgezogen:** Alle VLANs im Controller definiert (status=false = kein Management-IP, normal).
|
||
Traffic läuft noch auf VLAN 1 bis TP4 die L3-Interfaces + DHCP-Server je VLAN aktiviert.
|
||
|
||
<a id="tp2-1"></a>
|
||
|
||
### TP2.1 — Omada-Switch (sw2/ES208G) → Standalone-Recovery (kein Controller) ▸ ✅ FERTIG (2026-07-16, User solo)
|
||
- **Root Cause:** beim VLAN20-Umzug des Omada-Controllers (TP14.1, 2026-07-10) ist die Controller↔sw2-Verbindung zerbrochen (sw2 seither „Disconnected", ursprünglich als reiner Mgmt-Loss vertagt). Beim Rettungsversuch hat der User versehentlich einen **Werks-Reset** am Switch ausgelöst. Ein anschließender Wiederherstellungsversuch (durch Claude) hat Teile der Config **unbemerkt halluziniert** — der damalige „wiederhergestellte" Stand war **nicht vertrauenswürdig**.
|
||
- **Entscheidung (User 2026-07-15): Controller-Zustand als invalid betrachtet, sw2 komplett neu — diesmal OHNE Managementsoftware.** Kein Omada-Controller-Re-Adopt, kein Omada-Ecosystem-Anschluss (s. TP15-Korrektur — Omada bekommt generell keine Rolle mehr, auch nicht WLAN). sw2 direkt über eigenes Web-UI konfiguriert, wie ein eigenständiger Switch.
|
||
- **✅ 2026-07-16 (User-Aussage): „voll durch, Funktionsweise erfolgreich getestet."** Nach dem gescheiterten Claude-Versuch hat der User sw2 **komplett allein** neu aufgesetzt (Runbook `docs/tp2.1-omada-switch-standalone-runbook.md` als Playbook-Referenz, nicht Schritt-für-Schritt-Log). **Keine formale Einzelschritt-Verifikation dieser Session** (kein Port-für-Port-Protokoll, keine Live-Prüfung durch Claude) — Status beruht auf User-Bestätigung „es geht einfach", nicht auf eigener Nachkontrolle.
|
||
- **sw2 bleibt Solo:** „bekommt nie Geschwister" — keine weiteren Omada-Geräte geplant. Interimslösung bis zur Ablösung durch CRS326 (TP15).
|
||
- **Offen/nachzuziehen (nicht blockierend):** finale Portbelegung + IP von sw2 in `docs/ip-plan.md`/CLAUDE.md-Hardware-Tabelle nachtragen (aktuell dort noch „Adoption pending" — stale, aber Wert unbekannt, nicht geraten); `omada/.secrets` auf neue Standalone-Creds prüfen; ob Omada-Controller-Container jetzt komplett überflüssig ist.
|
||
- Abh.: TP1 ✅ (VLAN-Trunk am Uplink). Verwandt: TP15 (löst sw2 mittelfristig durch CRS326 ab), TP14.1 (Auslöser).
|
||
|
||
<a id="tp3"></a>
|
||
|
||
### TP3 — WLAN ▸ 📜 **VOLLSTÄNDIG HISTORISCH — der Ist-Stand steht in TP28, nicht hier**
|
||
> 🔴 **Lesehinweis (überarbeitet 2026-08-19): dieser gesamte TP ist Geschichte, von der ersten bis zur
|
||
> letzten Zeile.** Er beschreibt zwei überholte Zustände nacheinander — die **CAPsMAN-Ära (Juni/Juli
|
||
> 2026)** und danach den **Standalone-Rebuild vom 2026-08-08**. Beides gilt nicht mehr:
|
||
> seit **TP28 (2026-08-17)** läuft das WLAN wieder über **CAPsMAN, mit `crs326-core` als Manager**;
|
||
> ap1/ap2 sind reine CAPs ohne eigene WLAN-Konfiguration.
|
||
>
|
||
> ⚠️ **Bis 2026-08-19 trug dieser TP im Header „beide hAP ax² sind reine Standalone-APs, CAPsMAN
|
||
> abgeschafft" und der Lesehinweis verwies als „Ist-Stand" auf den 08-08-Eintrag.** Wer die WLAN-Frage
|
||
> hatte, landete zuerst hier — und bekam die falsche Antwort. Genau daran ist eine frische Session am
|
||
> 2026-08-19 hängengeblieben. **Für den aktuellen WLAN-Stand: TP28 lesen, für Funkparameter TP25.**
|
||
> Der Wert dieses TPs sind die Gotchas (VLAN-Leck ohne `datapath`, Bring-up-Fallen), nicht der Status.
|
||
- ✅ **5 sichtbare SSIDs live, beide APs, beide Bänder, je SSID→VLAN** (wifi datapath vlan-id):
|
||
`dirtyvader-main`(10, WPA3-only, FT), `-iot`(30, **nur 2,4 GHz**, WPA2+3), `-media`(40, WPA2+3, **FT=no** wg. Sonos),
|
||
`-untrusted`(50, WPA2+3), `-guest`(90, WPA2+3). Alt-SSIDs `install*`/`BF-Main`/`wv*` entfernt. Temp-PSK (User setzt echte).
|
||
- ✅ **CAPsMAN-Architektur:** ap1=Manager+AP (lokale Radios), ap2=CAP (dynamisch via Provisioning, `remote-cap=Ok`).
|
||
**Kernlektion:** Der Manager kann seine **eigenen** Radios NICHT über CAPsMAN provisionieren → ap1 lokal zuweisen
|
||
(Master via `set` + statische Slave-VAPs), ap2 zieht Master+Slaves dynamisch. Ein CAP hält **keine** Eigen-Config.
|
||
- ✅ **Kanal-Plan (non-DFS, überlappungsfrei):** ap1 ch36/5180 + ch1/2412 · ap2 ch149/5745 + ch11/2462.
|
||
(Auto-Channel hatte beide APs auf 5500-DFS/2437 gelegt = Co-Channel → fix gepinnt. ap2-Kanaländerung braucht
|
||
`remote-cap provision`-Push.) `bound`-Problem damit obsolet/sauber gelöst.
|
||
- ✅ **Sonos-Migration abgeschlossen (2026-07-02):** alle 10 Sonos auf `dirtyvader-media`/VLAN 40 mit MAC-fest
|
||
reservierten DHCP-Leases in `dhcp-v40`. Household geschlossen, iOS-App findet alle Geräte (mDNS via Repeater).
|
||
Inventar/IPs = SSoT `docs/sonos-inventar.md` (IPs haben sich verschoben — Arc jetzt `.199`, Arbeitszimmer-Paar auf `.191/.192`).
|
||
- **Vertagt:** hidden `dirtyvader-infra`(20)+`-mgmt`(99) (Mgmt verkabelt). **TP8:** eine 802.1X-SSID
|
||
(`dirtyvader-secure`) mit FreeRADIUS-Dynamic-VLAN → dann PSK-`main`/`infra` einstampfen (Konsolidierung).
|
||
- **SSID-Strategie-Entscheid (Begründung):** unterschiedl. WPA-Typ/Band erzwingen separate SSIDs; „alles in 1 SSID
|
||
per Auth" nur via 802.1X (Geräte ohne EAP wie Sonos/TV/IoT brauchen ohnehin PSK) → Hybrid in TP8.
|
||
- **Mesh = NEIN** (beide APs wired-trunk → Loop-Risiko). DoH-Upstream = Cloudflare Gateway (seit TP4 aktiv).
|
||
- Abh. erfüllt: TP4 ✅ (L3/VLAN-Interfaces). Folge: TP8 (802.1X), Sonos-Migration.
|
||
- 🔄 **CAPsMAN wird abgeschafft (User-Entscheid 2026-07-22) — WLAN-Neuaufbau from scratch.** Anlass: WLAN dauerhaft instabil, besonders wenn **beide** APs laufen. Beide APs bekommen künftig **eigenständige, von Nautobot Golden Config identisch generierte RouterOS-Configs** statt Manager/CAP.
|
||
- **Begründung (kein Widerspruch zu Nautobot, aber ein echter Reibungspunkt):** CAPsMAN erzeugt auf dem CAP **dynamische** Objekte (`wifi13–22`), die nie in dessen eigener Config stehen → Golden-Config-Compliance sieht auf ap2 faktisch nichts. Genau diese Halbheit war die Ursache von TP3.1 (CAPsMAN schrieb `datapath.vlan-id` nicht auf die Leitung und legte die tagged-Member am CAP nicht an).
|
||
- **Was NICHT verloren geht:** 802.11r/k/v braucht kein CAPsMAN, sondern nur gleiche SSID/PSK/Mobility-Domain → Fast-Roaming bleibt. Preis: Radio-/Kanaländerungen sind zwei Deployments statt eines Provision-Pushes — mit Nautobot ohnehin ein Job-Run.
|
||
- **Reihenfolge:** WLAN-Neuprovisionierung erst **nach** dem CRS326-Bring-up (TP15), da Routing/DHCP/DNS vorher umziehen.
|
||
- ✅ **DURCH (2026-08-08): beide APs als reine Standalone-APs neu provisioniert, CAPsMAN weg.** ap1 `10.40.99.11` (an core `ether15`), ap2 `10.40.99.12` (an branch `ether24`); Uplink geräteseitig auf **`ether1`** (PoE-Port, `poe-out=off`) umgestellt, ether2 aus dem Trunk entfernt. 6 SSIDs (main WPA3+FT / infra/iot/media/untrusted/guest WPA2+3, iot nur 2,4 GHz, media FT=no); **jede SSID mit eigenem `datapath` bridge+vlan-id** → das TP3.1-„cfg-main-2g ohne datapath"-Leck ist strukturell weg. Kanäle non-DFS ap1 ch36/ch1, ap2 ch149/ch11. Client-Test grün (VLAN10/30-Leases korrekt). Configs `routeros/.secrets/ap{1,2}-prov-base.rsc`, PSKs `routeros/.secrets/wlan-psks.env`, As-built → Nautobot via `nautobot/ingest-ap-rebuild.py`. **Einheitliches Admin-PW auf allen 4 MikroTik** (User-Vorgabe). Henne-Ei beim Bring-up: Werks-`192.168.88.1` über Core-Temp-Pfad (`192.168.88.2` + masq, wieder entfernt) via scp+`/import`; Werks-Inline-Overrides auf wifi1/wifi2 mussten geleert werden (`!configuration.ssid !security.*`), sonst funkt die Main-VAP die Werks-SSID. Offen: DoH auf core (nur plain 1.1.1.1), breiterer CLAUDE.md-Gotcha-Cleanup (ap1-als-L3-Router-Referenzen sind überholt).
|
||
|
||
<a id="tp3-1"></a>
|
||
|
||
### TP3.1 — ap2 VLAN-Trunk-Fix ▸ ✅ Config live (2026-07-01) — `routeros/tp3.1-ap2-vlan.rsc`
|
||
- **Root Cause:** ap2 (CAP, local forwarding) hatte `bridge vlan-filtering=no` → `datapath.vlan-id` wurde nie
|
||
auf die Leitung geschrieben → **ALLE** WLAN-Clients auf ap2 (main/media/iot/…) egressten **ungetaggt in VLAN1**
|
||
(defconf/`10.40.41.x`) statt ins Ziel-VLAN. Symptom: iPhone (VLAN10) im OG „verbunden, aber sporadisch kein
|
||
WLAN-Symbol" — beim Roaming ap1↔ap2 sprang es VLAN10↔VLAN1 (Subnetz-Flip → Abbruch). Sonos-Terrasse landete
|
||
auf ap2 fälschlich auf `10.40.41.183`. TP3 „ein CAP hält keine Config" gilt nur für Manager-Forwarding — hier
|
||
local forwarding, daher **braucht ap2 doch VLAN-Config**.
|
||
- ✅ **Fix (wie ap1):** ap2-Bridge `vlan-filtering=yes`; VLAN1 untagged=bridge,ether2 (Mgmt), VLANs 10/30/40/50/90
|
||
tagged auf ether2. Da nur die Master-Radios wifi1(5G)/wifi2(2.4G) Bridge-Ports sind (Slave-VAPs forwarden über
|
||
den Master), zusätzlich **wifi1/wifi2 als tagged-Member** je Client-VLAN (wifi1: 10/40/50/90, wifi2: 10/30/40/50/90).
|
||
CAPsMAN legt diese auf dem CAP **nicht** automatisch an → manuell gesetzt. Danach `remote-cap provision`.
|
||
- **Lockout-Risiko gehandhabt:** ap2-Mgmt (10.40.41.2) untagged VLAN1 blieb erhalten; kein Scheduler (device-mode=home)
|
||
→ Recovery = Power-Cycle OG. Ausgeführt im leeren-registration-table-Fenster.
|
||
- ⏳ **Offen — funktionaler OG-Test durch User:** Handy auf `dirtyvader-main` im OG → Lease muss `dhcp-v10`/`10.40.10.x`
|
||
sein (nicht `defconf`/`10.40.41.x`). Falls kein DHCP: Omada-Port-2-Trunk (tagged 10/30/40/50/90) prüfen.
|
||
- Abh.: TP3 ✅, TP4 ✅ (Bridge-VLAN-Muster gespiegelt). Config ap2-lokal (nicht CAPsMAN-verwaltet).
|
||
- 🔁 **Interim-Härtung 2026-07-06 (Migrations-Provisorium, fliegt mit VLAN1 raus):** Symptom „kein DHCP auf
|
||
`dirtyvader-main`, Altnetz ebenso, nichts geändert". Diagnose via MikroMCP (ap1+ap2): **Dual-DHCP-Race** — dieselben
|
||
Main-MACs (`56:71:6C:22:E6:A9`, `F2:62:4C:E1:AF:09`, `C0:C7:DB:DE:71:BE`) hielten gleichzeitig eine korrekte
|
||
VLAN10-Lease (`dhcp-v10`/`10.40.10.x`) **und** eine VLAN1-Lease (`defconf`/`10.40.41.18x`); `defconf` und `dhcp-v10`
|
||
boten parallel → `offering ... without success` + Minutentakt-Flapping. `bridge`-ARP auf ap1 = 333 Einträge (flache
|
||
VLAN1 lebt voll weiter; IoT/Shelly untagged auf `10.40.41.2xx`). **Griff:** auf ap2 die Master-Radio-Bridge-Ports
|
||
`wifi1`/`wifi2` von `frame-types=admit-all` auf **`admit-only-vlan-tagged`** — untagged Main/Slave-Frames werden
|
||
verworfen statt in VLAN1 gedumpt (im Zielbild ohnehin korrekt, daher kein Rückbau nötig, verschwindet mit VLAN1).
|
||
`ether2` bleibt `admit-all` (VLAN1-Mgmt/Trunk, kein Lockout). **Rollback:** `frame-types=admit-all` auf beiden Ports.
|
||
⚠️ **Rest-Leak bleibt: cudy1 (altes WLAN)** bridged Clients weiter untagged in VLAN1 — nur per Switch-Port-Tag bzw.
|
||
Alt-WLAN-Abbau (Migration) zu schließen, NICHT auf der MikroTik-Seite. Zusätzlich `st-main rrm=yes wnm=yes` (802.11k/v,
|
||
behandelt nur das Roaming-Symptom). SSoT-Detail: Memory `project_vlan1_dhcp_incident`.
|
||
- 🎯 **ROOT CAUSE des ap1-Lecks gefunden (2026-07-22, live aus `/interface/wifi/configuration print`) — es ist KEIN `frame-types`-Problem:** `cfg-main-2g` (SSID `dirtyvader-main` auf 2,4 GHz, gebunden an Master-Radio `wifi2`) hat **überhaupt keinen `datapath`-Block** — weder `bridge` noch `vlan-id`. Das 5-GHz-Pendant `cfg-main-5g`/`wifi1` taggt sauber nach VLAN 10, **2,4 GHz taggt gar nicht** → egress ungetaggt in VLAN 1. Ein Client, der zwischen den Bändern **derselben SSID** wechselt, springt damit `10.40.10.x` ↔ `10.40.41.x` — Subnetzwechsel mitten in der Verbindung. Erklärt das Dual-DHCP-Race und das „verbunden, aber sporadisch kein WLAN" ohne jede Zusatzannahme. **Fix nicht mehr direkt anwenden** — geht mit der WLAN-Neuprovisionierung (CAPsMAN-Abschaffung, TP3) über Nautobot ein. Nebenbefund: `cfg-infra` existiert auf ap1, wird von **keinem** Interface benutzt (deckt sich mit TP3.3), und ap1 funkt real auf **ch36/ch1** — die Interfaces überschreiben das Channel-Profil `ch-5g` (5745/ch149).
|
||
- 🔴 **Update 2026-07-15: gleiches Leck jetzt auf ap1 vermutet.** `dirtyvader-main`-Clients bekommen zeitweise
|
||
`10.40.41.x`-IPs statt `10.40.10.x` — identisches Dual-DHCP-Race-Muster wie oben, aber die 2026-07-06-Härtung
|
||
wurde **nur auf ap2** gemacht. Nächster Schritt: gleiche `admit-only-vlan-tagged`-Umstellung auf ap1s lokalen
|
||
Main-Radio-Bridge-Ports (⚠️ NICHT `ether2` anfassen — das ist ap1s Mgmt-/WAN-Trunk-Pfad). Diagnose+Fix-Schritte:
|
||
`docs/tp19-vlan1-cudy-teardown-runbook.md` Phase A (~30-60 Min). Diese Härtung ist wie die ap2-Variante ein
|
||
Provisorium, das mit TP19s VLAN1-Abbau automatisch obsolet wird.
|
||
|
||
<a id="tp3-2"></a>
|
||
|
||
### TP3.2 — WLAN Coverage / P750W-Discovery ▸ ✅ **ABGESCHLOSSEN (2026-08-17, User-Test erfolgreich)** — `docs/wifi-p750w-coverage-and-map.md`
|
||
- ✅ **Brother P750W (VLAN30) gelöst:** DHCP-`client-id`-Fix (matcht über MAC → feste `10.40.30.5`); **an ap1 gepinnt**
|
||
(geordnete wifi-access-list `accept w2-iot` → blanket `reject`, überlebt ap2-Re-Provision) — dozende Legacy-Clients
|
||
nur am co-located-GW ap1 per ARP erreichbar, CAP ap2 nicht; mDNS `vlan30` in `mdns-repeat-ifaces`+5353 → in MAIN
|
||
auffindbar. Druck+Selbsttest OK. Web-UI `http://10.40.30.5/general/information.html`.
|
||
- ✅ **Nebenbei:** `multicast-enhance=enabled` (iot+media), Guest `ft=yes`, INFRA-SSID(20) angelegt (**nur ap2**),
|
||
Steering-Profil `st-main`/`neighbor-group=bf-main` auf beide Main-Configs.
|
||
- **Nomenklatur:** dynamische ap2-CAP-Slaves (`wifi13–22`) nicht praktikabel umbenennbar (`name-format` unzuverlässig)
|
||
→ Map dokumentiert statt umbenannt. Backups `bf-ap{1,2}-pre-nomen-20260702.backup`/`.rsc` auf den Geräten.
|
||
- ⏳ **Offene Coverage-Arbeit → ausgelagert nach TP3.3** (Fortsetzung in neuer Session).
|
||
|
||
<a id="tp3-3"></a>
|
||
|
||
### TP3.3 — WLAN Coverage-Kur ▸ 📜 **IN TP25 AUFGEGANGEN (2026-08-17)** — offene Arbeit steht dort, nicht hier
|
||
> ⚠️ Der Header sagte bis 2026-08-19 „🔜 TODO (Fortsetzung, neue Session)" — dieselbe Falle wie bei TP3:
|
||
> ein Status, der nicht mitgezogen wurde. Der Re-Walk und die Dead-Spot-Kartierung sind **Schritt 5 von
|
||
> TP25** und zwar bewusst **nach** den Funkparameter-Fixes; die 802.11k/v-Reparatur ist am 2026-08-13
|
||
> erledigt worden. Was hier steht, ist Begruendung und Messhistorie.
|
||
- ✅ **BEHOBEN 2026-08-13, noch am selben Abend (User: „aus Zeitgründen zuerst 11k/v, der nächste Call steht an").** `st-main` (`rrm=yes wnm=yes neighbor-group=bf-main`) auf beiden APs wiederhergestellt und an `cfg-main-5g`/`cfg-main-2g` gehängt — **via GC-ConfigPlan**, bewusst nicht per Direkt-Write: der Push läuft so NAS→Kabel→AP und nicht über die WLAN-Verbindung, die man gerade umkonfiguriert. `/system backup` je AP davor (`ap{1,2}-pre-steering-20260813.backup`). **Gegengeprüft am Gerät** (nicht am Plan-Status): `st-main rrm=true wnm=true ng=bf-main`, beide Main-Configs verweisen darauf.
|
||
- ⚠️ **Erwartete Nebenwirkung: alle Clients haben kurz reassoziiert** (ap1 25→2, ap2 7→14 unmittelbar danach) — `steering=` auf einer `wifi configuration` zu setzen re-applied die VAPs, auch die nicht angefassten. Nach ~2 Min eingependelt (ap1 15 / ap2 13, VLAN30 = 28 Hosts auf der Core-Bridge). **Nicht während eines Calls machen.**
|
||
- ✅ **In GC verankert:** Steering steht jetzt im AP-Zweig des Templates (`st-main`/`bf-main` als Konstante — es gibt genau eine Roaming-Domain pro Standort) + neues ComplianceFeature **`wifi-steering`**. Eine künftige Neuprovisionierung kann es damit nicht mehr stillschweigend verlieren — genau das war der Fehler vom 2026-08-08.
|
||
- ⏳ **Offen: der Praxistest.** Dieselbe Route noch einmal gehen. Reißt es weiterhin ab, ist es echte Coverage → Dead-Spot-Kartierung.
|
||
- 🔴 **Der Befund (2026-08-13): Steering war auf BEIDEN APs weg.** `/interface/wifi/steering` = **0 Einträge** auf ap1 *und* ap2 (live geprüft). Vor dem Rebuild war `st-main` mit `rrm=yes wnm=yes` + `neighbor-group=bf-main` auf beiden Main-Configs gesetzt (s. TP3.2 + TP3.1-Interim). Die Neuprovisionierung am 2026-08-08 aus `routeros/.secrets/ap{1,2}-prov-base.rsc` hat es **nicht wieder angelegt**.
|
||
- **Wirkung:** kein **802.11k** (Neighbor Reports → der Client kennt die Nachbar-APs gar nicht) und kein **802.11v** (BSS Transition → der AP kann den Client nicht wegschicken). Der Client bleibt am sterbenden AP kleben, bis die Verbindung zusammenbricht, und muss dann komplett neu scannen + assoziieren = **harter Abriss statt Übergabe**. Genau das Symptom „einmal durch die Wohnung, WLAN weg" (User-Erlebnis 2026-08-13 während eines Telegram-Videocalls, musste auf 5G ausweichen).
|
||
- ⚠️ **`ft=yes` ist KEIN Gegenbeweis** (auf beiden APs gesetzt, `ft-mobility-domain` beidseitig leer = gleicher Default, FT selbst also intakt). **802.11r macht die Übergabe schnell, sobald der Client sie beschließt — es bringt ihn nicht dazu, sie zu beschließen.** Ohne 11k/v fällt die Entscheidung viel zu spät. „FT ist an" ≠ „Roaming funktioniert".
|
||
- **Reihenfolge-Konsequenz:** Diese Regression **vor** der Dead-Spot-Kartierung beheben und dieselbe Route nachlaufen — sonst kartiert man Löcher, die in Wahrheit Roaming-Verhalten sind, und zieht daraus womöglich Hardware-/Platzierungs-Entscheidungen (s. „ap1 ins Sideboard"-Idee unten).
|
||
- **Weg (Governance):** Steering gehört ins **GC-Template** (`/interface/wifi/steering` + `steering=` an der Main-Configuration) → feature-scoped Deploy. Kein Direkt-Write. Betrifft `wifi-configuration` (bereits Compliance-Feature) + ein neues Feature `wifi-steering`.
|
||
- 💡 **Idee „ap1 ersetzt den Sideboard-Switch" (User 2026-08-13):** ap1 hat 4 freie Ports (`ether2–5`; `ether3/4/5` sind bereits Bridge-Ports mit `pvid=1`, `ether2` ist seit der ether1-Umstellung **kein** Bridge-Port mehr und müsste wieder aufgenommen werden) → die Wohnzimmer-Multimediageräte könnten direkt an ap1, der Smart-Managed-Switch entfiele. **Technisch machbar** (untagged VLAN40 wie `core ether2`), **aber platzierungsgebunden:** ein Media-Sideboard ist RF-mäßig einer der schlechtesten AP-Standorte (geschlossen, Metallgehäuse der AV-Geräte, niedrig). **User-Vorgabe: Platzierung hat Vorrang vor dem eingesparten Gerät.** Entscheidung daher erst **nach** der Steering-Reparatur + Re-Walk.
|
||
**Problem:** Kanten/Terrasse grenzwertig — Clients (iPhone/iPad) landen bei **−76..−89 dBm** und springen zwischen
|
||
allen 4 Main-Radios (ap1-5G/2,4G ↔ ap2-5G/2,4G) → „0–1 Striche + häufiger Abriss"; **Terrasse auch mittags** schlecht.
|
||
FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
|
||
|
||
**Bereits geklärt/erledigt (nicht nochmal aufrollen):**
|
||
- NICHT config-verursacht (ap1 kein Reboot, RF-Konfig unverändert). **TX schon am AT-Max** (18/15 dBm, EIRP-Deckel — kein Hebel).
|
||
- Kanalplan intern überlappungsfrei (ap1 ch1/ch36, ap2 ch11/ch149). **Steering** `st-main`/`neighbor-group=bf-main`
|
||
auf beiden Main-Configs gesetzt (hilft Client-Entscheidung, macht Signal aber nicht stärker).
|
||
- **ap1-only ist vom Tisch** (echte Funklöcher) → beide APs nötig, aber besser platziert.
|
||
|
||
**Offene Arbeit:**
|
||
1. **Dead-Spot-Kartierung** (Tageslicht): User stellt sich in jeden Problempunkt (Terrasse zuerst, entfernte Räume,
|
||
dicke Wand) → je Punkt `registration-table` + `monitor` (Signal je Radio/Band/AP). Zusätzlich Nachbar-WLAN-Scan
|
||
`/interface/wifi/scan` je AP (**störend** — wirft Clients kurz ab) gegen Abend-Congestion. Output → SSoT-Doku.
|
||
2. **Remediation nach Befund:** Platzierung (ap2 Richtung Schwachseite umstellen / **3. AP** / Outdoor-Unit / Kabelweg);
|
||
Roaming-Feintuning (802.11v `transition`-Schwellen ab 7.21, Band-Overlap reduzieren); Kanalplan aus Scan nachziehen.
|
||
3. **INFRA(20) auf ap1 ergänzen** (fehlt — sendet nur auf ap2): lokaler `w2-`/`w5-infra`-Slave analog w2-/w5-media.
|
||
4. Danach Re-Walk-Test (wie 2026-07-02, aber mit Kartierung als Baseline).
|
||
|
||
**Sicherheitsnetz:** Backups `bf-ap{1,2}-pre-nomen-20260702.backup`/`.rsc` auf den Geräten. Kontext-Memory `project_wifi_coverage`
|
||
+ `ref_wifi_powersave_multicast`. Abh.: TP3 ✅, TP3.1 ✅, TP3.2 ✅.
|
||
|
||
<a id="tp4"></a>
|
||
|
||
### TP4 — Router-Cutover (hAP#1 = Router + WAN-Edge) ▸ ✅ FERTIG (2026-07-01) — ⚠️ **Rolle seit 2026-08-08 auf `crs326-core`** (TP21.1); alles unten beschreibt den damaligen ap1-Aufbau und gilt als **Historie/Referenz**, nicht als Ist
|
||
- ✅ **Bridge VLAN-Filtering aktiviert** (`vlan-filtering=yes`); VLAN-Tabelle: VLAN1 untagged bridge+ether2, VLANs 10/20/30/40/50/90/99/100 tagged bridge+ether2.
|
||
- ✅ **VLAN-Subinterfaces:** vlan10/20/30/40/50/90/99 auf bridge; je GW `.1` (10.40.10.1 … 10.40.99.1).
|
||
- ✅ **DHCP:** dhcp-v10…dhcp-v99 je auf eigenem VLAN-Interface, pools .100-.200, GW+DNS = VLAN-GW.
|
||
- ✅ **DNS:** RouterOS-nativ, allow-remote-requests=yes. **Upstream seit 2026-06-16: DoH Cloudflare Gateway** (`use-doh-server=https://px35ynlol3.cloudflare-gateway.com/dns-query`, verify-doh-cert, `servers=1.1.1.1` als Bootstrap; FB-Upstream abgelöst). Statische Einträge für alle Infra-Hosts unter `bf.lan` (13 Einträge, Transition-IPs bis PVID-Fix).
|
||
- ⚠️ Auto-Suffix `.lan` (RouterOS-Einschränkung, kann nicht auf leer gesetzt werden) — TP5-Cleanup.
|
||
- ✅ **NAT:** srcnat masquerade out-interface=vlan100 (ap1 = Public-Edge; kein Double-NAT mehr). Fallback-Konfig für ether1 bleibt reversibel dokumentiert (`routeros/wan-cutover-runbook.md`).
|
||
- ✅ **Routing-Tabelle:** 0.0.0.0/0 via Liwest-Gateway auf vlan100; alle 7 VLAN-Subnetze als Connected-Routes aktiv.
|
||
- ✅ **Edge-Cutover vollzogen (2026-07-01):** FritzBox 6660 physisch weg (Liwest hat gegen ein Arris-Kabelmodem im Bridge-Mode getauscht — kein Zurück). WAN = `Arris → sw1 gi16 Access VLAN100 → gi15 Trunk → ap1 ether2 → vlan100 auf bridge` (**NICHT** ether1). ap1 hält Public-IP direkt (Liwest, kein CGNAT). **WAN-Input-Seal live** (TP10-seal-Regeln: est/inv/dhcp/icmp/lan/wandrop im `input`-Chain, `list_firewall_rules` 2026-07-02 verifiziert). Telefonie war auf der FB und wird nicht weitergeführt (User-Bestätigung 2026-07-02).
|
||
- **DNS-Entscheidung** (Technitium **und** externer BIND verworfen): RouterOS-DNS als Resolver, keine DHCP→DNS-Auto-Registrierung (soweit möglich). Infra-Hosts als statische `/ip dns static`-Einträge unter `bf.lan`; dynamische Clients bewusst ohne Namen. Filtering/DNSSEC via DoH-Upstream (TP3). HA: 2. Resolver auf ap2, statische Einträge beidseitig pflegen.
|
||
- Abh.: TP1 ✅. TP3-Abhängigkeit entfällt (WLAN läuft weiterhin über FB bis TP3).
|
||
- **Ermöglicht jetzt:** PVID-Fix in Omada-Profilen (MAIN→PVID10, INFRA→PVID20), untagNetworkIds-Bereinigung, DHCP-Reservierungen für NAS/macmini/pve auf neue VLAN-Subnetze migrieren.
|
||
|
||
<a id="tp5"></a>
|
||
|
||
### TP5 — Firewall & Inter-VLAN-Policy ▸ ✅ **DURCH (2026-08-18): deny-by-default in BEIDEN Ketten scharf** · **+ E5 (2026-08-19)** — 📋 `docs/tp5-deny-by-default-runbook.md`
|
||
> 📌 **„DURCH" heißt nicht „abgeschlossen".** E5 (MAIN→IOT) kam einen Tag nach dem Drop — unter deny-by-default
|
||
> braucht **jeder** neue Dienst über eine VLAN-Grenze seine Regel. Weitere Etappen sind normaler Betrieb,
|
||
> kein Rückschritt. Der Weg dahin ist immer derselbe: **erst den Drop-Log lesen**, dann die Regel schreiben.
|
||
|
||
- ✅ **ETAPPE 5 (2026-08-19): `MAIN → IOT` aufgemacht — `routeros/tp5-e5-main-iot.rsc`, Backup `core-pre-tp5-e5-main-iot-20260819.backup`, Compliance danach 92/92, 0 Drifts.**
|
||
- **Auslöser (User):** Brother P750W und die Shellys aus VLAN10 nicht erreichbar. Der Drop arbeitete
|
||
korrekt — es gab schlicht **nie** eine `vlan10→vlan30`-Zeile. Offen waren nur `MAIN→MEDIA`,
|
||
`MAIN→INFRA`, `MAIN→MGMT`; IOT fehlte als einziges Paar in der Liste.
|
||
- 🔴 **Merkregel: der Drucker fällt mit `udp/161` auf, NICHT mit `631`.** Im Drop-Log stehen
|
||
40 Zeilen SNMP nach `10.40.30.5` und kein einziges IPP — macOS fragt zuerst den SNMP-Status ab
|
||
und bricht ab, bevor es überhaupt zu IPP kommt. **Wer nur 631 whitelistet, repariert nichts.**
|
||
- **Log-Matrix `in:vlan10 out:vlan30`:** 497 ICMP · 489 `udp/5353` · 489 `udp/137` ·
|
||
237 `udp/3722` (Apple-Continuity → `.30.201`) · 40 `udp/161`. Quellen: `.10.183` (1596 Zeilen,
|
||
Sweep über alle 254 Adressen), `.10.180` (118), `.10.82` (38).
|
||
- **Entscheid (User): voll einbahnig, kein Portfilter.** MAIN steht zu INFRA/MEDIA/MGMT ohnehin
|
||
ungefiltert offen — und INFRA hält NAS, Proxmox und Nautobot, also deutlich mehr Wert als die
|
||
Shellys. MAIN→IOT eng zu filtern wäre Symbolik, kostet aber bei jedem neuen IoT-Gerät eine Runde
|
||
Fehlersuche. **Der Sicherheitswert sitzt in der Gegenrichtung:** `IOT→MAIN` bekommt bewusst
|
||
**kein** `accept` und bleibt gedroppt; Antworten laufen über `defconf: accept est,rel,untracked`.
|
||
- **Abnahme am Gerät** (von `10.40.10.82`, also der Quelle aus dem Log): `.30.5` ICMP 0 % Loss,
|
||
`snmpget sysName.0` → `P750W`, tcp/631+9100+80 offen; `.30.202/.203/.221` HTTP **200**.
|
||
`.30.201` bleibt still — kein Firewall-Thema mehr (die Regel ist ungefiltert), Gerät aus.
|
||
- ⚠️ **Bewusste Nebenwirkung:** der Subnetz-Sweep von `.10.183` läuft ab jetzt durch statt im
|
||
Drop-Log aufzulaufen. Betriebsneutral, aber der Log wird an dieser Stelle leiser — **nicht mit
|
||
„Problem gelöst" verwechseln**, wenn später jemand die Log-Rate als Metrik liest.
|
||
- 🔧 **Werkzeug-Fix nebenbei: `gc-manual-plan.py --assert` URL-encodet die Query jetzt.** Ein
|
||
Kommentar mit Leerzeichen (`TP5-E5: MAIN->IOT`) warf `http.client InvalidURL` — **nach** dem
|
||
erfolgreichen Deploy, d.h. der Change war durch und nur die Gegenprüfung crashte. Das ist die
|
||
schlechtere Reihenfolge: man steht mit einem Traceback da und weiß nicht, ob es gewirkt hat.
|
||
|
||
- ✅ **ETAPPE 3 + 4 DURCH (2026-08-18, nachts).** `forward` und `input` enden jetzt mit `drop`, die
|
||
Log-Regel steht in beiden Ketten **davor** und protokolliert weiter, was der Drop frisst.
|
||
Artefakte: `routeros/tp5-etappe2-nachtrag.rsc`, `-etappe3-drop.rsc`, `-etappe4-input-mgmt.rsc`,
|
||
`-etappe4-nachtrag-discovery.rsc` (+ die beiden autorevert-abbestellen-Plaene).
|
||
Backups `core-pre-tp5-{nachtrag,etappe3,etappe4}-20260818.backup`.
|
||
- 🔴 **Das 70-Sekunden-Ergebnis aus Etappe 2 (die Log-Regel sei verstummt) war falsch.** Ueber
|
||
dreieinhalb Stunden gemessen liefen **119 Zeilen** auf, in vier Gruppen — drei davon haetten den
|
||
Drop zum Ausfall gemacht:
|
||
| Zeilen | Was | Warum es die Whitelist nicht traf |
|
||
|---|---|---|
|
||
| 92 | Matter `udp/5540`, SwitchBot `.30.227` → HA | die E2-Regel `IOT->INFRA Dienste` ist **TCP-only** |
|
||
| 18 | **`10.40.20.31`** → `.99.{2,11,12}:22` | der Netmiko-Deploy kommt vom **`nautobot-worker`**, nicht vom Web-Container `.20.30`, auf den die E2-Regel lautete (deren Zaehler stand auf 0) |
|
||
| 8 | `.99.x:8728` → HA | Rueckrichtung des Mgmt-Pfads, nach Conntrack-Ablauf als `new` |
|
||
| 1 | Shelly `.30.225:80` → HA | gleiches Muster |
|
||
➡️ **Merkregel: ein Zaehlerstand ueber 70 Sekunden ist keine Messung.** Und wer den Deploy-Pfad
|
||
whitelistet, muss die IP des **Workers** nehmen, nicht die des Web-Containers.
|
||
- 🔴 **Fund in Etappe 4, der erst spaeter zugeschlagen haette: die CAPs finden ihren Manager per
|
||
Layer-2-Discovery.** `/interface/wifi/cap` auf ap1 zeigt `discovery-interfaces=vlan99` und
|
||
`current-caps-man-address: 04:F4:1C:B4:17:76%vlan99` — eine **MAC**, keine IP. Der input-Drop
|
||
verwarf MNDP (`udp/5678`); bestehende CAPWAP-Sitzungen liefen als `established` unbeeindruckt
|
||
weiter, kaputt gewesen waere es **erst beim naechsten AP-Reboot**. Geschlossen mit
|
||
`TP5-E4: MNDP discovery` (LAN) + `TP5-E4: CAPsMAN capwap` (nur vlan99).
|
||
- 📌 **`input` ist der riskantere Teil, nicht `forward`:** der Core ist DNS- **und** DHCP-Server fuer
|
||
alle sieben VLANs, und beides lief allein ueber `TP10-seal-lan`. Reihenfolge deshalb zwingend
|
||
Ersatzregeln → `seal-lan disabled=yes` → Drop. `seal-lan` wurde **abgeschaltet, nicht geloescht**
|
||
(schnellerer Rueckweg, und der Auto-Revert greift genau dort an).
|
||
- ✅ **Nebeneffekt wie vorhergesagt: die 5 `TP5: mDNS in vlanXX`-Input-Regeln sind endlich scharf**
|
||
(vlan30 = 87, vlan20 = 13 Pakete binnen Minuten; sie standen seit dem 2026-08-14 auf 0).
|
||
- 🎯 **Abnahme am Geraet:** `forward`-Drop **11 Pakete in 15 Minuten — ausschliesslich die eigene
|
||
pve→branch:22-Sonde**; `input`-Drop nur Broadcast-Rauschen (MNDP auf VLAN1-nativ, NetBIOS
|
||
137/138 aus VLAN20, SSDP-Announcements einer Sonos aus VLAN40). Haus im Normalbetrieb:
|
||
23 Leases, 30 WLAN-Clients, beide CAPs `Ok`, WAN steht. Funktionstest: Shelly aus INFRA `200`,
|
||
Internet `301`, MacBook erreichbar, `branch:22` von pve aus **zu** (korrekt).
|
||
**Und der Pfadbeweis: ein GC-Deploy auf ap2 lief MIT scharfem Drop durch.**
|
||
- 🛟 **Auto-Revert-Scheduler** (10 min, Name ohne Bindestrich) lief vor jedem der beiden Schnitte
|
||
und wurde nach bestandener Abnahme per eigenem Plan abbestellt.
|
||
- 🔜 **Bleibt offen:** die **DNS-Bypass-Sperre** — sie gehoert bewusst nicht hierher, sondern an das
|
||
Ende von TP23 (Stufe 6), wenn die Ausnahme-Oberflaeche nachweislich traegt.
|
||
|
||
- ✅ **ETAPPE 2 DURCH (2026-08-17) — `routeros/tp5-etappe2-whitelists.rsc`, ConfigPlan `86fbd528`, Backup `core-pre-tp5-etappe2-20260817.backup`.**
|
||
- **Basis waren 43.027 Logzeilen** aus dem Fenster 2026-08-16T17:21 → 2026-08-17T14:05 (Kollektor `10.40.20.18`),
|
||
also **~21 Stunden echter Nutzverkehr** statt der 10-Minuten-Stichprobe von Etappe 1. Die vollständige Matrix:
|
||
| Paar | Zeilen | Dienste (Ziel:Port, absteigend) |
|
||
|---|---|---|
|
||
| `vlan20 → vlan99` | **20.682** | `8728` auf branch (7468) / ap1 (7468) / ap2 (5731) = HA-`mikrotik`-Integration · `:22` je 8 = Nautobot-Netmiko |
|
||
| `vlan10 → vlan20` | **8.082** | macmini `udp/3722` **6234** (Continuity) · NAS `6690` 1578 · Nautobot `8080` 137 · AirPlay `7000` 69 · pve-mcp `3000` 22 · HA `9583` 16 · pve `:22` 12 · macmini `49152`/`udp 59107` je 8 · NAS `:22` 4 · **Ollama `11434`** 2 |
|
||
| `vlan20 → vlan10` | **7.123** | Rückrichtung Continuity (eigene Flows je Richtung) |
|
||
| `vlan20 → vlan30` | **6.238** | HA pollt ~22 Shellys auf `tcp/80` |
|
||
| `vlan30 → vlan20` | 306 | **`.30.249` → HA `8123`** (203) · macmini `49152` (1) |
|
||
| `vlan10 → vlan30` | 237 | **nur** `udp/3722` → `.30.201` = das iPhone im IOT-SSID (D2) |
|
||
| `vlan10 → vlan99` | 214 | Arbeitsplatz `.10.82` → branch/ap1/ap2 `:22`/`:80`/`:443` |
|
||
| `cf-mesh → *` | **137** | `100.96.0.1` → Nautobot `8080` (62) · NAS `6690` (46) · macmini/NAS/branch/ap1/ap2 `:22` · pve `8006` · pve-mcp `3000` |
|
||
| `vlan10 → vlan10` | 17 | same-VLAN mDNS `5353` (vom Repeater auf dem Core) |
|
||
| **`vlan40`/`vlan50`/`vlan90`** | **0** | ➡️ **D6 bestätigt: Internet-only-Pinning ist risikofrei.** `out:bridge` kommt **nicht mehr vor** (VLAN1-Rückbau hält). |
|
||
- **Entscheidungen (User 2026-08-17):** **D1 = MAIN→INFRA pauschal** (MAIN *ist* das Trusted-VLAN; 13 Einzelports
|
||
wären eine Wartungsfalle, die bei jedem neuen Dienst still bricht). **D4 = MGMT named sources** — MAIN komplett
|
||
+ HA `.20.105:8728` + Nautobot `.20.30:22` + `cf-mesh`, Rest drop. **D5** = beobachtetes `8123` + MQTT als Absicht;
|
||
**Influx `8086` NICHT** (nie beobachtet, der Weg läuft über HA).
|
||
- 🎯 **ABNAHME am Gerät bestanden:** 11 Regeln (`*1B`–`*25`) existieren, `disabled=false`, **alle vor der Log-Regel**
|
||
(`place-before`, sonst wäre das Gate unten nicht messbar), Selbstprotokoll `tp5e2: ENDE whitelist-regeln=11`.
|
||
**Und der Zähler der Log-Regel steht: 54.129 bei 14:17:06 und unverändert bei 14:18:13; die letzte Zeile im
|
||
Kollektor ist `14:17:16` — genau der Deploy-Zeitpunkt.** Die Regel ist also **verstummt**.
|
||
- ⚠️ **Aber das ist noch KEIN Freibrief für Etappe 3.** 70 Sekunden Stille sind keine Aussage — das Runbook
|
||
verlangt bewusst „einige Tage", weil Geräte existieren, die **einmal pro Woche** funken (Backup-Jobs,
|
||
Drucker-Discovery). **Gate für Etappe 3: mehrtägiges Fenster ohne neue Zeilen.**
|
||
- 📌 **Zwei Dinge müssen VOR dem Drop passieren:** (a) **D2** — iPhone `.30.201` ins MAIN-SSID umhängen
|
||
(Hands-on Christoph); solange es im IOT-SSID hängt, killt der Drop die 237 `udp/3722`-Flows. Eine Whitelist
|
||
darauf wäre instabil, weil iOS **pro SSID** eine eigene Private Address vergibt. (b) `seal-lan` verengen (D4
|
||
`input`-Teil), was zugleich die 5 toten mDNS-`input`-Regeln scharf schaltet.
|
||
- ✅ **ABNAHME DES VLAN1-RÜCKBAUS BESTANDEN (2026-08-16): `out:bridge` = 0.** 10-Minuten-Messfenster nach dem Wiederanschalten, Logdatei vorher geleert. Vorher waren `vlan20 → bridge` **76 %** des gesamten Inter-VLAN-Verkehrs. Die Rate fiel von ~166 auf ~34 Zeilen/min — der Rückbau hat das Log auf echten Nutzverkehr eingedampft. Wiederaufnahme über `routeros/tp5-etappe1-resume.rsc` (GC, Backup `core-pre-tp5-resume-20260815.backup`).
|
||
- 📊 **Beobachtete Matrix, 339 Zeilen / 10 min** — das Arbeitsmaterial für Etappe 2:
|
||
|
||
| Paar | Zeilen | was genau |
|
||
|---|---|---|
|
||
| `vlan20 → vlan99` | 126 | **HA `10.40.20.105` → `10.40.99.2:8728` + `10.40.99.11:8728`** (je 67) = der Management-Pfad |
|
||
| `vlan20 → vlan30` | 85 | HA → IoT: `.30.211:80` (44), `.30.8:10081` (22), `.30.244:8080` Nuki (22), `.30.5:631` Drucker (4) |
|
||
| `vlan10 → vlan20` | 65 | Arbeitsplatz → INFRA |
|
||
| `vlan20 → vlan10` | 61 | INFRA → MAIN |
|
||
| `vlan10 → vlan10` | 2 | **mDNS `10.40.10.183:5353 → 10.40.10.82:5353`** — s.u. |
|
||
|
||
- 🔑 **D4 ist damit belegt, nicht mehr geschätzt:** der Management-Pfad ist **HA** (`10.40.20.105`), das über seine vier `mikrotik`-Integrationen `8728` auf branch/ap1/ap2 pollt (core `.99.1` erscheint nicht, weil das `input` ist, nicht `forward`). Dazu MikroMCP vom Arbeitsplatz. **Ohne Whitelist sperrt der spätere Drop die Verwaltung des eigenen Netzes aus.**
|
||
- ⚠️ **Neuer Merkposten für Etappe 3 — same-VLAN-Verkehr erreicht die `forward`-Kette:** `vlan10 → vlan10`, unicast **mDNS** zwischen zwei MAIN-Clients, erzeugt vom mDNS-Repeater auf dem Core. Zwei Pakete in 10 Minuten, harmlos — **aber ein blanker Drop am Kettenende würde sie treffen** und damit Discovery *innerhalb* von MAIN beschädigen. Zusammen mit `accept out-interface-list=WAN` die zweite Regel, die bekannt ist und nicht aus dem Log wiederentdeckt werden muss.
|
||
- ✅ **D2 vollzogen:** iPhone ist im MAIN-SSID, `10.40.30.201` steht im ARP auf `failed`.
|
||
- ✅ **Etappe 1 ausgerollt 2026-08-14** (über Nautobot GC, manual Config Plans; Backups `core-pre-/post-tp5-etappe1-20260814.backup`). Auf dem Gerät: `forward`-Regel `action=log log-prefix="TP5-WOULD-DROP"` am Kettenende, Syslog-Action `tp5syslog` → **Kollektor `10.40.20.18:514`** (syslog-ng-Container `tp5-syslog` auf dem Docker-LXC, Logs `/opt/tp5-syslog/log/`, 10-Minuten-Cron kappt bei 500 MB — der LXC hat nur ~2,5 GB frei). `firewall` ist aus dem Memory-Ring genommen (`topics=info,!firewall`), sonst wäre der On-Box-Log tagelang unbrauchbar. **Blockiert nichts.** Artefakte: `routeros/tp5-etappe1-log-lauf.rsc`, Push-Werkzeug `nautobot/gc-manual-plan.py`. Compliance danach **16/17** (der eine Rotpunkt `ip-address` = bekannte VLAN1-Bring-up-IP `10.40.41.211` aus dem TP22-Backlog, **keine neue Drift**).
|
||
- 🔴 **Fund aus dem Log-Lauf, der Etappe 3 bestimmt: die Regel fing anfangs auch allen Internet-Verkehr** (`out:vlan100`, ~¼ der Zeilen in einer ruhigen Minute, unter Last mehr). Die Log-Regel steht deshalb jetzt auf `out-interface-list="!WAN"`. **Konsequenz für den späteren Drop: er darf NICHT bare ans Kettenende** — davor gehört `accept out-interface-list=WAN`, sonst ist das ganze Haus offline. Diese Whitelist-Zeile ist damit bekannt und muss nicht aus dem Log wiederentdeckt werden.
|
||
- 🔍 **Erste Befunde nach 60 s** (Rate nach Einengung ~166 Zeilen/min): **76 % des Inter-VLAN-Verkehrs ist `vlan20 → bridge`, also INFRA nach VLAN1** — ein Monitoring-Host `10.40.20.151` scrapet ~10 tote/alte `10.40.41.x`-Ziele, HA `.20.105` zieht auf `10.40.41.100:8006` (pve unter seiner **alten** VLAN1-Adresse, real `.20.100`), `.20.107` auf `10.40.41.10:8080`. **Das ist TP19-Material, das TP5 gerade sichtbar macht.** Weiter: `vlan20 → vlan99:8728` (MikroTik-API auf branch/ap1/ap2 = **der Management-Pfad selbst**, muss in die Whitelist, betrifft D4), `vlan20 → vlan30` (HA → Drucker `.30.5:631`, Shelly, Nuki `.30.244:8080`), `vlan10 ↔ vlan20` (Synology Drive `6690`, Apple-Continuity `udp/3722`).
|
||
- ✅ **D2 entschieden (User 2026-08-14): iPhone wird ins MAIN-SSID umgehängt.** Faktenlage war: `10.40.30.201`, dynamische Lease, Hostname `iPhone`, MAC `12:99:A6:AC:B0:CB` = iOS Private Wi-Fi Address, aktiv auf `dirtyvader-iot` mit 748 MB rx. Die statische Reservierung `10.40.10.196` „iPhone" (andere private MAC — iOS vergibt **pro SSID** eine eigene) belegt, dass es dasselbe Telefon ist. **Umhängen ist die Hands-on-Aufgabe von Christoph** (SSID am Gerät wechseln + `dirtyvader-iot` vergessen lassen); technisches Argument: eine Whitelist auf eine rotierende Private MAC wäre ohnehin instabil.
|
||
- 📋 **Session-Runbook liegt (2026-08-14): `docs/tp5-deny-by-default-runbook.md`** — Ist-Stand live erhoben, **beobachtete Inter-VLAN-Flow-Matrix** (conntrack + Counter), 6 offene Entscheidungen (D1–D6), 4 Etappen mit Absicherung, Verifikation + Rollback. **Kernansatz: Etappe 1 ist ein reiner Log-Lauf ohne Blockierung** (`action=log` ans Ende von `forward`) — die Whitelist wird aus dem Log abgeleitet, nicht am Schreibtisch geraten. Drei Funde, die vorab eine Entscheidung brauchen: ein **iPhone sitzt in VLAN30**, ein **ICMP-Dauersweep von `.10.82` auf alle Shellys**, und **vlan99 ist Mitglied der `LAN`-Liste** (→ `seal-lan` erlaubt Mgmt-Input aus jedem VLAN). **VLAN50/90 tragen null Inter-VLAN-Verkehr** → Internet-only-Pinning ist dort risikofrei.
|
||
- **Deny-by-default + least-privilege** (Ziel). MAIN→INFRA nur Whitelist-Dienste; MAIN↔MEDIA allow (+mDNS); UNTRUSTED/GUEST nur Internet; MGMT isoliert; Router-input gehärtet.
|
||
- ⚠️ **Gerätewechsel beachten (2026-08-14 nachgetragen):** der ✅-Block unten war ursprünglich **auf ap1** verifiziert (2026-07-02) — ap1 ist seit 2026-08-08 Werksreset und reiner AP. Die Regeln sind beim Core-Cutover (TP21.1) mitgewandert, waren am neuen Gerät aber **nie gegengeprüft**. Am **2026-08-14 auf `crs326-core` live nachverifiziert** (`list_firewall_rules`, chains `forward` 13 Regeln / `input` 12 Regeln): **alle unten gelisteten Regeln existieren, sind enabled, tragen ihre `TP5:`/`TP10-seal-`-Kommentare und zählen Traffic.** Merkregel: nach jedem Rollenwechsel eines Geräts gilt eine Verifikation vom Vorgänger nicht weiter.
|
||
- ✅ **Bereits live** (ap1 2026-07-02 → **re-verifiziert auf `crs326-core` 2026-08-14**):
|
||
- `forward accept vlan10↔vlan40` — TP5: MAIN↔MEDIA (AirPlay/Sonos)
|
||
- `forward accept vlan20↔vlan40` — TP5: INFRA↔MEDIA (HA↔Sonos)
|
||
- `input accept udp/5353 vlan10/vlan20/vlan30/vlan40` — mDNS-Repeater (TP7-Anschluss)
|
||
- `forward accept vlan90↔vlan40` + `input accept udp/5353 vlan90` — **GUEST→MEDIA (Sonos/Cast), permanent** (TP7.2, 2026-07-03)
|
||
- **WAN-Input-Seal komplett** (TP10-seal-est/inv/dhcp/icmp/lan/wandrop + WG-Hub UDP/51821 accept)
|
||
- ⏳ **Offen:** Deny-by-default am Ende von `forward` (**live bestätigt 2026-08-14: die `forward`-Kette endet nach `TP5: MEDIA->GUEST` ohne Drop → alles nicht explizit Erlaubte läuft durch**); UNTRUSTED/GUEST auf Internet-only pinnen; MGMT isolieren; IoT→INFRA-Whitelist bauen.
|
||
- 🔍 **Befund beim Re-Verify (2026-08-14) — die 5 `TP5: mDNS in vlanXX`-Input-Regeln sind derzeit tot:** sie stehen **hinter** `TP10-seal-lan` (`in-interface-list=LAN`, action=accept), das jeden LAN-Input schon vorher annimmt → Counter aller 5 mDNS-Regeln stehen auf **0 Pakete**, während `seal-lan` 3,5 Mio. zählt. **Kein Fehler und nichts zu tun** — sie sind die Vorbereitung auf genau den Schritt, der noch aussteht: sobald der Router-`input` gehärtet wird (`seal-lan` verengen/entfernen), werden sie scharf. **Falle für die TP5-Session: erst `seal-lan` verengen, DANN testen** — vorher beweist ein grüner mDNS-Test gar nichts, weil er über `seal-lan` läuft.
|
||
- 📌 **Ausgangslage für die Umsetzung (Live-Stand `crs326-core`, 2026-08-14):** `forward` = defconf (est/rel/untracked accept · fasttrack · drop invalid · drop WAN !dstnat) → 2× TP10-WG → 6× TP5-Holes (MAIN↔MEDIA, INFRA↔MEDIA, GUEST↔MEDIA), **kein abschließender Drop**. `input` = TP10-seal komplett (est/inv/dhcp/icmp/lan/wandrop) + WG-Listen 51821 + 5× mDNS. Der Deny-by-default-Griff ist damit **eine Regel am Ende von `forward`** plus die Whitelists davor — nicht ein Umbau der Kette. ⚠️ **Governance:** läuft über Nautobot GC (`ip-firewall-*`-Features aus TP22), **nicht** per Direkt-Write; vorher `/system backup` (TP22-Regel 3). ⚠️ **Rettungspfad:** ein zu früh gesetzter Drop killt Mgmt/DHCP/DNS quer durchs Haus — Reihenfolge Whitelists zuerst, Drop zuletzt, und `device-mode` auf core erlaubt scheduler → **Auto-Revert-Scheduler als Sicherung ist hier möglich** (anders als auf den hAPs).
|
||
- 🔊 **Im Paket enthalten (User-Vorgabe 2026-08-14): der macOS-Sonos-Client-Fix.** Ist-Stand: **iOS-App findet alles** (mDNS via Repeater ✅), **HA steuert/gruppiert sauber** (Sonos-Panel, via static hosts + `advertise_addr`, TP7.2 ✅) — **nur die macOS-Desktop-App findet die Sonos-Umgebung nicht.** Ursache ist bekannt und **keine Firewall-Frage**: die Desktop-App entdeckt per **SSDP (UDP 1900)**, und RouterOS reflektiert **nur mDNS, kein SSDP** → die Discovery quert die VLAN-Grenze MAIN(10)→MEDIA(40) nicht. Die `forward accept vlan10↔vlan40`-Regel ist bereits offen und hilft hier nicht, weil das Problem *vor* dem Verbindungsaufbau liegt. **Fix = SSDP-Reflector (TP7.1)** auf NAS/pve (nicht am Router — kein Flash) mit Bein in vlan10 + vlan40, plus `input accept udp/1900` auf den bedienten VLANs. **TP7.1 wird damit aus „vorgemerkt" in dieses Arbeitspaket gezogen** und ist mit der Deny-by-default-Arbeit gemeinsam zu testen (sonst prüft man zweimal dieselbe Strecke).
|
||
- **IoT→INFRA-Rückmeldung (deine Frage):** IoT darf **aktiv** zu INFRA-Diensten initiieren — Firewall-Allow IoT→INFRA NUR auf **MQTT (1883/8883), HA-API, InfluxDB (8086)** o.ä.; Antworten via established/related; IoT→MAIN bleibt deny. So pusht ein Sensor Daten an HA/MQTT/Influx, ohne dass IoT „frei" ist.
|
||
- Abh.: TP4 ✅ (L3-Basis, Rolle inzwischen auf `crs326-core`), **TP14.5 ✅** (Freigabe-Gate), **TP22 ✅** (GC-Deploy-Pipeline = der vorgeschriebene Weg für den Push). **Keine offenen Blocker mehr — TP5 ist der kritische Pfad.** Folge: **TP6 (CAKE)**. Hängt dran: **TP5.1** (DNS-Redirect/Torch für Low-Trust).
|
||
|
||
<a id="tp6"></a>
|
||
|
||
### TP6 — QoS/CAKE + Call-Quality-A/B-Test ▸ ✅ **GESCHLOSSEN 2026-08-23 — CAKE gebaut, gemessen, wieder ausgebaut.** ⚠️ **Die Erklaerung darin ist von TP6.1 ueberholt: nicht DOCSIS, sondern fehlende Klassentrennung.**
|
||
> 🔴 **ZUERST LESEN — diese Analyse ist teilweise widerlegt (2026-08-23, durch TP6.1).**
|
||
> Unten steht, die ~30 ms entstuenden im DOCSIS-Uplink und seien unabaenderlich. **Sie sind es
|
||
> nicht.** TP6.1 holt dieselbe Strecke unter derselben Saettigung auf **11,0 ms** — allein durch
|
||
> Klassentrennung mit Prioritaet. Der Fehler lag in der **Methode** hier: Bulk und Messverkehr
|
||
> kamen vom **selben Host**, damit gab es fuer CAKEs `triple-isolate` nichts zu trennen, und
|
||
> blosses Shapen aendert keine Reihenfolge. **Weiter gueltig bleibt:** dass CAKE als *eine*
|
||
> Queue ueber allem nichts brachte (dreimal gemessen), und der Befund zur Queue-Platzierung
|
||
> (Interface-HTB tot, Global-HTB wirkt). Der Rest dieses Abschnitts ist als Messprotokoll zu
|
||
> lesen, nicht als Ursachenanalyse.
|
||
> 🔴 **A/B gemessen, gleicher Client, gleiche Methode, Minuten auseinander:**
|
||
>
|
||
> | Konfiguration | avg Latenz unter Upload-Last |
|
||
> |---|---|
|
||
> | ohne CAKE | **42,4 ms** |
|
||
> | CAKE 8,5 Mbit (88 % der Linerate) | 43,3 ms |
|
||
> | CAKE 7 Mbit (73 %) | 44,0 ms |
|
||
> | **CAKE 5 Mbit (52 %)** | **44,6 ms** |
|
||
>
|
||
> idle 12,9 ms. **Selbst bei halber Linerate aendert sich nichts** — und dort muss die
|
||
> Warteschlange zwingend im Router liegen, der Modem-Puffer also leer bleiben. Die ~30 ms
|
||
> Zusatzlatenz entstehen damit **stromabwaerts**: im Arris/DOCSIS-Uplink, wo das
|
||
> Request/Grant-Scheduling auf MAC-Layer sitzt und gar keine Warteschlange ist.
|
||
> **Das kann keine Queue im Router beheben.** Nebenbefund: die max-Werte *steigen* mit
|
||
> haerterer Drosselung (53 → 94 → 148 ms) — die eigene Queue wird laenger, die DOCSIS-Latenz
|
||
> bleibt. ⚖️ **Empfehlung: TP6 schliessen — nicht mangels Nutzen, sondern weil das Werkzeug
|
||
> nicht an die Ursache reicht.** Das ist ein staerkeres Ergebnis als „marginal“ von 2026-06-16.
|
||
>
|
||
> ✅ **Dass CAKE technisch funktioniert, ist dabei bewiesen, nicht angenommen:** Global-HTB
|
||
> zaehlte 6.040.563 Bytes, und bei `cake-bandwidth=7000000` fiel der Durchsatz von 9,3 auf
|
||
> **7,78 Mbit**. Sie zaehlt *und* drosselt. Es scheitert nicht am Bau.
|
||
>
|
||
> 🔴 **KORREKTUR der ersten Fassung dieses Abschnitts (ebenfalls 2026-08-23):** dort stand
|
||
> „der CRS326 fuehrt keine Software-Queue“ — **das war falsch** und stuetzte sich auf zwei
|
||
> eigene Testfehler. Richtig ist praeziser:
|
||
> - **`/queue tree parent=<interface>` ist bei hw-offloadetem out-interface wirkungslos.**
|
||
> MikroTik dokumentiert das: *„Interface HTB will not work correctly when the out-interface
|
||
> is hardware offloaded and the bridge Fast Path is not active.“* Das war die echte Ursache
|
||
> der ersten, wirkungslosen Queue auf `parent=vlan100`.
|
||
> - **`/queue tree parent=global` + `packet-mark` wirkt sehr wohl** — die HTB Global sitzt im
|
||
> IP-Stack, nicht an der Interface-Queue. Rezept: `mangle chain=postrouting
|
||
> out-interface=vlan100 action=mark-packet` (postrouting laeuft **vor** src-nat, die
|
||
> LAN-Quelle ist dort noch sichtbar), dann Tree auf `parent=global`.
|
||
> - **Die Simple-Queue-„Gegenprobe“ auf `10.40.0.0/16` war untauglich:** die Reihenfolge ist
|
||
> mangle postrouting → **src-nat** → HTB Global → simple queues. Zum Zeitpunkt der Simple
|
||
> Queue ist die Quelle laengst die WAN-IP; das Target konnte nie matchen.
|
||
> - `ipv4-fast-path-active: no` und `ipv4-fasttrack-packets: 0` belegen zudem: die CPU sieht
|
||
> jedes Paket im Slow Path. Die Hardware war nie die Grenze.
|
||
> Details und die restlichen Sackgassen: Memory `ref_queue_htb_platzierung_docsis`.
|
||
>
|
||
> ✅ **Was aus beiden Anlaeufen bleibt:** die **ComplianceRule `queue`** (Feature + Rule,
|
||
> `match_config` = `/queue type` + `/queue tree`) schliesst die blinde Stelle aus §4.1 —
|
||
> dieselbe, die TP26 fuer `/ip firewall filter` geschlossen hat. Wirksamkeit bewiesen: erst
|
||
> 99/100 mit der fehlenden Queue als `missing`, nach dem Deploy 100/100. Zaehler jetzt
|
||
> **25 Rules x 4 Geraeten = 100 Checks**.
|
||
> ⚠️ **Am Geraet steht weiterhin die wirkungslose Interface-Queue** (`qos-wan-up` auf
|
||
> `parent=vlan100`) — in der SoT, compliant, **aber ohne jede Funktion**. Sie gehoert entfernt,
|
||
> sobald der Abschluss entschieden ist; bis dahin NICHT fuer aktives QoS halten.
|
||
>
|
||
> 💡 **Falls QoS hier je wieder Thema wird:** weder `hw-offload` opfern noch Chip-QoS ist
|
||
> noetig — der Global-HTB-Weg funktioniert ohne beides. Es lohnt nur nicht.
|
||
|
||
*(Vorheriger Kopf, als Messbefund weiter gueltig:)* 🟡 **GEMESSEN 2026-08-20/21 — Fall B, aber „messbar, nicht hörbar"**
|
||
> ✅ **Die Prämisse ist geprüft. Ergebnis + Rohdaten: [`tp6-praemisse-messrunbook.md`](tp6-praemisse-messrunbook.md) §5.**
|
||
>
|
||
> | | idle / C0 | Upload-Last | Download-Last |
|
||
> |---|---|---|---|
|
||
> | Durchlauf 1 (ohne Call) | 12,9 ms | **60,4 ms** | 41,3 ms |
|
||
> | Ort 1 Küche (mit Call) | 13,2 ms | **42,8 ms** | 37,1 ms |
|
||
> | Ort 2 Terrasse (mit Call) | 12,8 ms | **40,4 ms** | 36,3 ms |
|
||
> | Referenz 2026-06-16 | 12 ms | 17,5 ms | *nie gemessen* |
|
||
>
|
||
> **Linerate erstmals dokumentiert: Upload 9,6 Mbit · Download 54,6 Mbit.**
|
||
> Der Anstieg liegt in der Senderichtung bei **+28 bis +47 ms** und damit über der §3-Schwelle von
|
||
> ≲30 ms → **formal Fall B, CAKE ist begründet.** 🔴 **Aber:** durch alle sechs Lastphasen lief ein
|
||
> Telegram-Videocall, und es wurde **kein einziger Problem-Button gedrückt**. Messbar ja, wahrnehmbar
|
||
> nein. waveform gibt zeitgleich **Grade A** — kein Widerspruch, sondern ein kürzerer Test (Erklärung
|
||
> in §5.3). **Empfehlung: TP6 bleibt offen und begründet, hat aber niedrigere Priorität als TP25** —
|
||
> was die Calls tatsächlich zerreißt, sind die WLAN-Übergaben, und die repariert keine Queue.
|
||
> Wenn gebaut wird: **erst `/queue` ins GC-Template + ComplianceRule** (§4.1), dann die Queue, dann
|
||
> dieselbe Messung als A/B-Nachweis.
|
||
> 📋 **Eigene Session, Einstieg: `docs/handoff-tp6-session.md`** (Übergabe 2026-08-19),
|
||
> Arbeitsplan: `docs/tp6-praemisse-messrunbook.md`. Abbruchkriterium steht **vor** der Messung fest.
|
||
- 🔴 **Der Widerspruch, der zuerst aufzulösen ist:** dieser TP wird seit Monaten als „DAS Ziel" geführt —
|
||
aber `docs/call-quality-diagnosis.md` hat den Bufferbloat **bereits gemessen** und kommt zum
|
||
gegenteiligen Schluss. Wörtlich in dessen Ergebnistabelle:
|
||
`CAKE / QoS | kein Bufferbloat-Spielraum | ❌ nicht deployt (wäre marginal)`, und im Kopf:
|
||
TP6 „hat sich als **Nicht-QoS-Problem** entpuppt".
|
||
**Messwerte (2026-06-16):** 3 Flows = **10,3 Mbit** Upload gesättigt · idle **12 ms** ·
|
||
Vollast avg **17,5 ms** / max **26 ms** → **A/A+**. Bei solchen Zahlen holt CAKE nichts mehr.
|
||
- ⚖️ **Was das relativiert:** die Messung stammt aus der **FritzBox-Ära**. Seither ist der Edge komplett
|
||
getauscht (Arris im Bridge-Mode, NAT und Queuing liegen auf `crs326-core`). Die Leitung ist dieselbe,
|
||
das Pufferverhalten des neuen Edge ist **ungemessen**.
|
||
- ✅ **ERLEDIGT 2026-08-20/21 — gemessen statt vermutet** (die frühere Fassung dieser Zeile verlangte
|
||
genau das): der Bufferbloat-Test gegen den heutigen Edge ist gefahren, dreimal, davon zweimal mit
|
||
laufendem Videocall an verschiedenen Orten. Es ist **schlechter geworden als 2026-06-16** (60,4 statt
|
||
17,5 ms avg unter derselben Sättigung), die Ursache ist damit neu und liegt **nicht** an der Core-CPU
|
||
(~10 % unter Upload-Last). Zahlen und Einordnung: Runbook §5.
|
||
- 🔎 **Fasttrack ist auf dem Core faktisch tot — und das ist kein Entwurf, sondern ein Unfall**
|
||
(Befund 2026-08-18): die Regel `defconf: accept est,rel,untracked` steht **vor**
|
||
`defconf: fasttrack`, also wird Fasttrack nie erreicht. Live: **32.979.171 Pakete** auf der
|
||
accept-Regel, **0** auf der Fasttrack-Regel, **0 von 445** Verbindungen fasttracked.
|
||
- **Herkunft:** `ap1` hatte die Reihenfolge vor dem Cutover **richtig** (Fasttrack zuerst, wie im
|
||
MikroTik-defconf). Umgedreht wurde sie beim handgebauten Core-Aufbau (TP21.1).
|
||
- **Für TP6 ist das günstig:** Fasttrack umgeht die Queues, CAKE braucht sie — ein Konflikt, den man
|
||
sonst erst auflösen müsste, existiert hier gar nicht.
|
||
- ✅ **ENTSCHIEDEN (Christoph, 2026-08-19): der Zustand bleibt — und gilt ab jetzt als Absicht, nicht
|
||
als Unfall.** Begründung: bei ~10 Mbit WAN kostet der Verzicht nichts (die Core-CPU trägt den
|
||
Forward-Pfad ohnehin, 32,9 Mio Pakete sind der Beleg), und für CAKE ist er die bequemere Ausgangslage.
|
||
Damit ist auch die TP21-Warnung „das Re-Baseline hat den Fehler zum Soll erklärt" **aufgelöst**: die
|
||
Reihenfolge in der SoT ist jetzt gewollt. Sie war es bei der Aufnahme nur nicht.
|
||
- ⚠️ **Gegenbedingung, unter der neu bewertet wird:** wenn **Inter-VLAN** je auf Gigabit soll
|
||
(NAS ↔ MAIN läuft geroutet über dieselbe 800-MHz-CPU), ist der Verzicht teuer. **Die Messung dafür
|
||
steht aus** — und sie, nicht die WAN-Zahl, ist die Vorbedingung jeder Neubewertung.
|
||
- ⚠️ **Ungemessen ist der Preis:** ohne Fasttrack läuft **jedes** geroutete Paket durch die volle
|
||
Kette samt Conntrack, auf der Core-CPU. Für 10 Mbit WAN irrelevant, für **Inter-VLAN**-Transfers
|
||
(NAS ↔ MAIN, Synology Drive) möglicherweise nicht. Das ist messbar und sollte gemessen werden,
|
||
bevor jemand die Reihenfolge „repariert".
|
||
- 🔴 **Und es steht seit 2026-08-18 als ABSICHT in der SoT:** das Re-Baseline
|
||
(`ingest-core-firewall.py --apply`) hat die Reihenfolge unverändert nach Nautobot geschrieben,
|
||
Rule `ip-firewall-filter` mit `config_ordered=true`. ~~**Entscheidung nötig:** Reihenfolge korrigieren
|
||
(Fasttrack lebt wieder) · Fasttrack-Regel entfernen (ehrlich: wir nutzen sie nicht) · so lassen und
|
||
hier begründet stehen lassen.~~ → **entschieden am 2026-08-19, s. oben: so lassen.** Damit stimmen
|
||
SoT und Absicht wieder überein — bisher stimmten sie nur zufällig überein.
|
||
- 📋 **Runbook liegt bereit: `docs/tp6-praemisse-messrunbook.md`** (2026-08-19). Es reproduziert die
|
||
Methode von 2026-06-16 (3 Flows + 24 Pings idle/Last), misst erstmals auch die **Download-Richtung**
|
||
(die 2026-06-16 **nie** gemessen wurde — beim asymmetrischen Kabelanschluss die wahrscheinlichere
|
||
Problemseite), und trägt das Abbruchkriterium **vor** der Messung: A/A+ → TP6 wird **geschlossen statt
|
||
gebaut**. Enthält außerdem den Bau-Kontext für den Fall B, damit er nicht neu recherchiert wird —
|
||
vor allem: **`/queue` fehlt im GC-Template und hat keine ComplianceRule** (dieselbe blinde Stelle, die
|
||
TP26 für `/ip firewall filter` geschlossen hat), eine handgesetzte Queue wäre also driftend und
|
||
ungeschützt.
|
||
- 📌 **Nicht im Repo dokumentiert und deshalb Teil der Messung: die tatsächliche Liwest-Linerate.**
|
||
Die „~10 Mbit" stammen aus der Sättigungsmessung von 2026-06-16, nicht aus einem Tarifdatenblatt.
|
||
Ohne diese Zahl ist die übliche CAKE-Vorgabe „85–95 % Linerate" geraten.
|
||
- **Abh.:** TP5 ✅ (keine Blocker mehr). Vorgelagert: die Messung oben.
|
||
|
||
<a id="tp6-1"></a>
|
||
|
||
### TP6.1 — Saettigung vermeiden statt Bufferbloat bekaempfen ▸ ✅ **LIVE UND GEMESSEN (2026-08-23): 40,8 → 11,0 ms unter Last**
|
||
> **Der Gedanke kam von Christoph** beim TP6-Abschluss („dann koennten wir zumindest den Sync
|
||
> von der NAS zurueckhalten“) — und er hat mein TP6-Urteil zu Recht angezweifelt.
|
||
>
|
||
> 🟢 **Abnahme, echte Saettigung, Kontrollmessung inklusive:**
|
||
>
|
||
> | gleiche NAS-Last, gleicher Messclient | avg | max |
|
||
> |---|---|---|
|
||
> | Priorisierung **aus** | **40,8 ms** | 53,4 ms |
|
||
> | Priorisierung **an** | **11,0 ms** | 14,6 ms |
|
||
>
|
||
> **−73 %.** Und die Last war echt, nicht behauptet: `wan-up-total` fuhr **9,0 Mbit**
|
||
> (das max-limit greift exakt), `wan-up-bulk` 8,87 Mbit mit **1581 Drops** — der Sync wurde
|
||
> aktiv gebremst, waehrend `wan-up-rest` frei durchlief. `vlan100 tx = 9,0 Mbps`.
|
||
> Die Kontrolle mit abgeschalteter Markierung war noetig, um auszuschliessen, dass die
|
||
> Verbesserung bloss daher kommt, dass Bulk und Ping jetzt von verschiedenen Hosts stammen.
|
||
>
|
||
> 🔴 **Damit ist die DOCSIS-Erklaerung aus TP6 widerlegt.** Dort stand, die ~30 ms
|
||
> entstuenden stromabwaerts im Uplink und seien unabaenderlich. Sie sind es nicht — sie
|
||
> loesen sich auf, sobald der Verkehr in **getrennte Klassen** faellt. Der Fehler steckte in
|
||
> der TP6-**Methode**: Bulk und Messverkehr kamen vom **selben Host**, also gab es fuer CAKEs
|
||
> Host-Fairness (`triple-isolate`) nichts zu trennen — alles lag in derselben Klasse, und
|
||
> Shapen allein aendert an einer Reihenfolge nichts. Was CAKE nicht schaffte, schafft
|
||
> **explizite Klassentrennung mit Prioritaet**. Merkregel: nicht die Bandbreite ist das
|
||
> Problem, sondern **wer vor wem drankommt**.
|
||
>
|
||
> **Was gebaut ist** (Template [11a], via GC, 104/104 compliant):
|
||
> - zwei `mangle`-Regeln in `postrouting` (**vor** src-nat — nur dort ist die LAN-Quelle noch
|
||
> sichtbar): NAS → `bulkup`, alles andere → `restup`
|
||
> - `wan-up-total` (`parent=global`, `max-limit=9M`) mit zwei Klassen darunter:
|
||
> `wan-up-rest` `priority=1 limit-at=6M` und `wan-up-bulk` `priority=8 limit-at=1M`
|
||
>
|
||
> **Drei Entscheidungen, die begruendet bleiben sollen:**
|
||
> 1. **Prioritaet statt hartem Cap** — ein Limit haette die NAS auch nachts gebremst und
|
||
> Bandbreite verschenkt (DSM drosselt von sich aus nicht, User-bestaetigt 2026-08-23).
|
||
> `limit-at=1M` sorgt dafuer, dass der Sync nie ganz verhungert.
|
||
> 2. **`src-address=10.40.20.10`, nicht VLAN20** — in INFRA haengen auch HA, Nautobot und
|
||
> pve. Wer das je auf `10.40.20.0/24` erweitert, verlangsamt seine eigenen Werkzeuge.
|
||
> 3. **`parent=global`, nicht `parent=vlan100`** — Interface-HTB ist auf diesem Geraet tot
|
||
> (hw-offloadetes out-interface, s. TP6).
|
||
>
|
||
> ✅ **Governance-Vorbedingung erfuellt:** ComplianceRule **`ip-firewall-mangle`** angelegt
|
||
> (`nautobot/ingest-mangle-rule.py`), **`config_ordered=True`** — die NAS-Regel muss vor dem
|
||
> Catch-all stehen, sonst waere die Priorisierung **still** wirkungslos. Zaehler jetzt
|
||
> **26 Rules x 4 Geraeten = 104 Checks**.
|
||
>
|
||
> 💡 **Offen, klein:** ob weitere Bulk-Quellen dazugehoeren (pve-Backups? Time Machine?).
|
||
> Erst messen, ob sie ueberhaupt saettigen — die Regel dafuer ist eine Zeile.
|
||
|
||
<a id="tp7"></a>
|
||
|
||
### TP7 — Media-Discovery (mDNS) für VLAN 40 ▸ ✅ FERTIG (2026-07-02) — `docs/tp7-mdns.md`
|
||
- ✅ **Built-in mDNS-Repeater** aktiv — **Live-Stand `crs326-core` (verifiziert 2026-08-14):**
|
||
`mdns-repeat-ifaces=vlan10,vlan20,vlan30,vlan40,vlan90` → MAIN/INFRA/IOT/GUEST ↔ MEDIA. Reversibel (Feld leeren).
|
||
📌 **`bridge` (=VLAN1) ist nicht mehr dabei** — mit dem VLAN1-Retirement erwartbar und korrekt, war aber nie nachgezogen.
|
||
*Historische Fassung (ap1, bis 2026-08-08): `…,vlan90,bridge` — `bridge`=VLAN-1 seit TP7.2 2026-07-03.*
|
||
Realbestand: MAIN=iOS-Client + MacBook, INFRA=Home Assistant, MEDIA=10× Sonos (siehe `docs/sonos-inventar.md`).
|
||
- ✅ **Funktional durchgetestet (2026-07-02):** iOS-Sonos-App (vlan10) **und** Home Assistant (vlan20) finden die
|
||
9 online-Sonos per `_sonos._tcp.local.` (Bonjour) automatisch. Household nicht mehr gesplittet.
|
||
- ✅ **TP5-Anschluss live:** `forward accept vlan10↔vlan40` + `forward accept vlan20↔vlan40` + `input accept udp/5353`
|
||
auf vlan10/20/30/40 sind gebaut (mit „TP5:"-Kommentar), damit Discovery + Steuerung + Streaming ohne Umwege gehen.
|
||
- ⚠️ **SSDP/Cast (UDP 1900) bewusst NICHT gebaut** (bräuchte Reflector-Container auf NAS/pve; hAP hat kein USB/Flash).
|
||
Konsequenz: **macOS-Desktop-Sonos-App nutzt SSDP → quert die VLAN-Grenze NICHT** und findet die Sonos nicht.
|
||
iOS-App (mDNS) und HA-Integration decken den Nutzungsbedarf ab; Spotify Connect läuft über Cloud.
|
||
- Abh.: TP4 ✅, TP5 (Sonos-Holes) ✅ live.
|
||
|
||
<a id="tp7-1"></a>
|
||
|
||
### TP7.1 — SSDP-Relay (macOS-Sonos-App über VLAN-Grenze) ▸ ✅ **LÄUFT (2026-08-18)** — User-bestätigt: „sonos ist da!"
|
||
- ✅ **Umsetzung:** eigener LXC **108 `mcast-relay`** auf pve (Ubuntu 24.04, 1 Kern, 256 MB, `onboot`),
|
||
zweibeinig: `eth0` = **10.40.10.21** (VLAN10), `eth1` = **10.40.40.21** (VLAN40), Default-Route nur
|
||
über VLAN10, **kein IP-Forwarding**. Dazu `core ether13` (pve-Port) um `tagged vlan10, vlan40`
|
||
erweitert (`routeros/tp7.1-core-ether13-trunk.rsc`). Software: `alsmith/multicast-relay` als
|
||
systemd-Unit `multicast-relay.service`.
|
||
- **`--noMDNS` ist Pflicht:** mDNS reflektiert bereits der Core (TP7). Zweimal zu reflektieren
|
||
erzeugt Duplikate. ~~**`--ssdpUnicastAddr 10.40.10.21`** ist ebenfalls noetig — die Sonos
|
||
antworten auf einen M-SEARCH per **Unicast**, und diese Antworten muessen zurueckgereicht werden.~~
|
||
🔴 **WIDERRUFEN 2026-08-25:** die Flagge war der Fehler — sie schreibt in `multicast-relay.py` Z. 630
|
||
die Quelle jedes SSDP-Pakets um, worauf Z. 691 die M-SEARCH beim Senden auf `eth1` **selbst verwirft**
|
||
(`10.40.10.21` ist nicht im VLAN40-Netz). Der Hinweg lief damit **nie**; ausgebaut, Rückweg wird
|
||
geroutet (`TP5: MEDIA->MAIN`). Stand + Merkregeln: Ist-Stand in `teilprojekte.md` und Gotcha in `CLAUDE.md`.
|
||
- 🔑 **Warum zwei Beine zwingend sind** (Christophs Frage „vlan40 sollte keinen relay benoetigen?!?"):
|
||
SSDP ist **link-lokales Multicast mit TTL 1**. Der M-SEARCH des MacBooks verlaesst VLAN10 nie, die
|
||
`ssdp:alive`-NOTIFYs der Sonos verlassen VLAN40 nie. Ein Relay kann nur kopieren, was es selbst
|
||
hoert — es **muss** Mitglied beider Broadcast-Domaenen sein. Routing hilft wegen TTL 1 nicht, und
|
||
`forward accept vlan10<->vlan40` ist laengst offen: es scheitert **vor** dem Verbindungsaufbau, an
|
||
der Entdeckung. Dass AirPlay funktionierte, passt genau ins Bild — das ist mDNS.
|
||
- 🎯 **Beweis im Log:** `[Sonos Discovery] Relayed 268 bytes from 10.40.10.183:49358 on eth0 [ttl 64]
|
||
to 255.255.255.255:1900 via eth1/10.40.40.21` — echte Suchrufe beider Macs (`.10.183`, `.10.82`).
|
||
- ⚠️ **Falle beim Selbsttest:** ein M-SEARCH **vom Relay-Host selbst** wird nicht weitergereicht
|
||
(Loop-Schutz: das Werkzeug ignoriert eigene Quelladressen). Mein synthetischer Test meldete
|
||
deshalb 0 Antworten, waehrend die App bereits funktionierte. **Aus dem Relay heraus laesst sich
|
||
der Relay nicht pruefen** — entweder am echten Client testen oder am Log mitlesen.
|
||
- **Keine Firewall-Arbeit noetig:** der Relay spricht in beiden VLANs jeweils lokal; das trifft die
|
||
`forward`-Kette nicht.
|
||
- 📌 **Status-Update 2026-08-14:** Symptom vom User erneut bestätigt — **iOS-App ✅, HA-Sonos-Panel ✅ (steuern/gruppieren), macOS-Desktop-App findet die Umgebung nicht.** Damit ist das Bild vollständig: alles, was **mDNS** spricht, funktioniert; nur der **SSDP**-Weg fehlt. Der Fix wird **Bestandteil des TP5-Pakets** (gemeinsamer Test mit Deny-by-default statt zweimal dieselbe Strecke prüfen). ⚠️ **Nicht als Firewall-Problem fehldiagnostizieren:** `forward accept vlan10↔vlan40` ist längst offen — es scheitert an der Discovery, nicht am Datenpfad.
|
||
- **Bedarf:** macOS-Desktop-Sonos-App auf VLAN 10 findet die Sonos in VLAN 40 nicht (Discovery per SSDP UDP 1900,
|
||
RouterOS reflektiert kein SSDP). iOS-App + HA decken den Alltag ab, aber User will die Desktop-App nutzen.
|
||
- **Lösung:** kleiner SSDP-Reflector-Container mit Bein je Ziel-VLAN. Auf **NAS oder pve** (nicht am hAP, 128 MB
|
||
Flash + kein USB). Kandidaten: `bkupreck/ssdp-repeater`, `scyto/multicast-relay`, oder eigenes Python-Skript.
|
||
- **Scope:** vlan10 ↔ vlan40 (MacBook → Sonos). Optional vlan20 ↔ vlan40 falls HA je SSDP-Discovery brauchen sollte
|
||
(aktuell nicht — HA nutzt zeroconf/mDNS).
|
||
- **Anschluss TP5:** `input accept udp/1900` auf den bedienten VLANs (falls Router-lokal reflektiert wird) bzw.
|
||
Container-Port-Freigaben je Bein.
|
||
- **Alternative (verworfen):** macOS-Client temporär in VLAN 40 hängen (unpraktikabel für Alltagsnutzung).
|
||
- Abh.: TP7 ✅. Kein Blocker für TP5-Deny-by-default.
|
||
|
||
<a id="tp7-2"></a>
|
||
|
||
### TP7.2 — HA↔Sonos static-host binding + VLAN1/GUEST-Discovery ▸ ✅ FERTIG (2026-07-03) — `docs/sonos-inventar.md`
|
||
- **Root Cause (widerlegt frühere Doku):** HA-`media_player.*`-Sonos alle `unavailable`. HA sitzt **nicht** auf
|
||
VLAN 20, sondern noch auf `10.40.41.105` (VLAN-1-Transfernetz, IP-Migration steht aus). Der mDNS-Repeater
|
||
deckte VLAN 1 **nicht** ab, und die HA-Sonos-Integration ist **SSDP-abhängig** (`manifest dependencies:[ssdp]`,
|
||
soco 0.31.1) → quert die VLAN-Grenze nicht. Die alten „HA nutzt mDNS/zeroconf, fixed 2026-07-02"-Notizen waren falsch.
|
||
- ✅ **HA-Fix (discovery-unabhängig):** `sonos: media_player: hosts:` mit allen 11 Sonos-IPs + `advertise_addr: 10.40.41.105`
|
||
in `configuration.yaml` (Backup `.bak-sonos-20260703`). `advertise_addr` fixt die UPnP-Event-Callbacks trotz
|
||
HA-Multi-Homing (`.105`/`172.16.0.18`/docker). Core-Restart → alle 6 Online-Zonen `idle/playing` (Wohnzimmer-Push
|
||
verifiziert = Callbacks OK). Terrasse `.198` offline = kein Entity (kommt beim Einschalten). Kurzes Flapping direkt
|
||
nach Restart = Settling, danach stabil.
|
||
- ✅ **Nebeneffekt:** mit `bridge` im Repeater (s.u.) meldet HA jetzt „Automatic discovery is working, hosts not needed"
|
||
— die statischen Hosts sind also Redundanz (Gürtel+Hosenträger), bleiben aber als Absicherung.
|
||
- ✅ **VLAN 1 (temp) + GUEST (perm) Discovery/Reach (User-Wunsch 2026-07-03):**
|
||
- mDNS-Repeater erweitert → `mdns-repeat-ifaces=vlan10,vlan20,vlan40,vlan30,vlan90,bridge` (VLAN-1-untagged=`bridge`).
|
||
- Firewall: `input accept udp/5353 vlan90` + `forward accept vlan90↔vlan40` (ganzes MEDIA, beide Richtungen; User-Wahl).
|
||
VLAN 1/`bridge` brauchte nichts (schon via `TP10-seal-lan` + permissiver Forward).
|
||
- **Bewusst in Kauf:** flacher Repeater → Guests sehen auch MAIN/INFRA/IOT-mDNS-Announcements (Discovery-Leak;
|
||
echte Verbindungen regelt TP5-Deny-by-default).
|
||
- **Offen/später:** HA→VLAN 20 (IP-Migration nach Omada-PVID-Fix) → dann nur `advertise_addr` auf neue IP; macOS-Desktop-App
|
||
weiter via SSDP-Reflector TP7.1. Abh.: TP7 ✅, TP5-Holes ✅.
|
||
|
||
<a id="tp9"></a>
|
||
|
||
### TP9 — Gateway-/Router-HA ▸ ❌ OBSOLET (User-Entscheid 2026-08-08)
|
||
- ❌ **Verworfen (User-Begründung 2026-08-08, „ganz klar nein"):** am `crs326-core` führt ohnehin **nie ein Weg vorbei** — er ist der unumgängliche L3-Durchleitungspunkt. Ein zweites Gerät / VRRP **beseitigt diesen SPOF nicht**, sondern baut nur **zusätzliche Sollbruchstellen** ein (2× Config-Sync für DHCP/DNS/Firewall/NAT, Split-Brain-Risiko, VRRP-Eigenfehler) → netto ein Zuverlässigkeits-**Verlust**, kein -Gewinn. Die Frage ist nicht „Aufwand vs. Nutzen", sondern „mehr Fehlerquellen einbauen?" → nein. DR-Weg = Config-Backup-Restore (+ ggf. AP-Reprovisionierung). VRRP-Skizze unten = nur noch historische Recherche.
|
||
- **„Was, wenn ap1 ausfällt?"** → **VRRP** löst Gateway-Failover je VLAN ~gratis (kreuzweise Master/Backup).
|
||
⚠️ **Haken (nicht Last):** DHCP/Firewall/NAT/DNS müssen auf **BEIDEN** APs **identisch** gepflegt sein, sonst „Gateway up, Internet tot".
|
||
**Empfehlung:** VRRP nur für **MAIN/INFRA**, sonst ap2 als **Kalt-Ersatz** + Backup. DHCP/DNS schon redundant (Split + 2 Resolver).
|
||
- Abh.: TP4. (Research erledigt, s. TP-R + Config-Skizze im Report.)
|
||
|
||
<a id="tp10"></a>
|
||
|
||
### TP10 — Multi-Site VPN ▸ ✅ KERN DURCH (2026-06-27) — beide WG live auf ap1-Hub (`docs/tp10-vpn-migration.md`)
|
||
> **Details/Cutover/Gotchas/Eckdaten → `docs/tp10-vpn-migration.md` (Single Source of Truth).** Research: `docs/research/vpn-comparison.md`.
|
||
- **✅ KERN DURCH (2026-06-27) + EDGE-CUTOVER (2026-07-01):** beide AVM-Tunnel → **2× WireGuard mit Hub auf ap1** migriert. Live: `peer fm`
|
||
(`10.101.0.0/24`, bidirektional, myfritz-Endpoint) **und** `peer dw` (`10.95.10.0/24`, CGNAT-Dial-in) mit frischem
|
||
Handshake + Traffic. **Public-Edge direkt** — ap1 hört UDP/51821 auf `vlan100` (Edge-Cutover 2026-07-01), keine
|
||
FB-Portfreigabe mehr, kein Double-NAT. FM-IPsec gekillt, Gelis alter FB↔FB-WG sauber geswappt.
|
||
- **✅ DW/Geli on-site verifiziert (2026-06-27):** Geli auf `10.95.10.0/24` renumbert, FB-WG single `10.95.10.1/24`,
|
||
Keepalive 25, Endpoint `bf4…:51821`. End-to-End von macmini/Linz → Gelis 24/7-APs `10.95.10.246/.247` sauber
|
||
(echte LAN-IPs, **kein `10.255.255.x`**-NAT-Leak).
|
||
- **💡 Root Cause (FM-PoC bewiesen):** FB masqueradet WG, wenn die Interface-`Address` eine **Transfernetz-IP** ist →
|
||
muss die **FB-LAN-IP** sein (AVM Proxy-ARP). War der Schlüssel zum sauberen LAN-LAN.
|
||
- **Hub:** `wg-hub` (UDP 51821, Key `P/bzt…`, MTU 1412, keine IF-IP). Endpoint = DDNS **`bf4.emsenhuber.net:51821`**
|
||
(Cloudflare, DNS-only → Public-IP). Apply gebündelt in `routeros/tp10-cutover-apply.rsc`.
|
||
- ✅ **Permanenter Edge-Cutover durch (2026-07-01)** — Details in TP4 (WAN via sw1 vlan100, ap1 = Public-Edge,
|
||
WAN-Input-Seal live). BF-FB-eigenen WG-Server 51820 entfällt (FB physisch weg).
|
||
- **Offen:** DDNS-Auto-Updater für `bf4.emsenhuber.net` auf NAS/pve, damit der A-Record der Public-IP folgt
|
||
(falls Liwest sie tauscht). Aktuell manuell in Cloudflare gepflegt.
|
||
|
||
<a id="tp14"></a>
|
||
|
||
### TP14 — Host/Container-IP-Migration VLAN1→VLAN20 (Docker-Cutover NAS+pve) ▸ ✅ DURCH (2026-07-13; live re-verifiziert 2026-08-08) — `docs/nas-vlan20/RUNBOOK.md`, Handoff `docs/nas-vlan20/handoff/`
|
||
- ✅ **ABGESCHLOSSEN — Migration komplett** (Live-Check 2026-08-08 vom core: NAS `.20.10`, pve `.20.100`, Docker-LXC `.20.126`, platform-pg `.20.20`, Nautobot `.20.30` alle 0% Loss). Alle Hosts + Container auf VLAN20. **TP14.4** (CF-Access-Token-Rotation) = kleiner, nicht-blockierender Standalone-Follow-up (CF/TP11-Umfeld); **TP14.5** war nie ein eigener Change (nur ein Reihenfolge-Gate) → erfüllt, TP5 damit entblockt.
|
||
- ✅ **NAS (DS923, ep14) migriert (2026-07-09/10):** Docker von `default_external` (macvlan .41) auf **ein** `vlan20`-Macvlan (10.40.20.0/24, gw .1, parent `ovs_eth0`); DSM-Cutover `.20.10`; Portainer-Endpoint `.20.10:9001`. **Root-Cause der Dauerfehler** = Portainer **Config-Only-Macvlan-Falle** (`Driver:null`, kein Container-Attach) + Macvlan-Parent-Exklusivität (`default_external` muss weg für `vlan20`) + `default_internal` (172.18) weg. Container laufen auf `.20.x`.
|
||
- ✅ **CF-Ingress v116 (2026-07-10):** 6 NAS-Origins .41→.20 (SSoT `docs/cf-tunnel-mapping.md` §8); `vw` 200 via Tunnel = pve→VLAN20-Routing steht (502 direkt nach Ingress-PUT = transient).
|
||
- ⚠️ **Abh./Reihenfolge:** verzahnt mit Omada-PVID-Fix/IP-Migration (Reihenfolge #3); Sessions = Sub-TPs **TP14.1–14.5**, sequenziell A→B→C→(D jederzeit)→E, **keine parallelen Branches** (geteilte Live-Infra). Anschlussstatements: `docs/nas-vlan20/handoff/README.md`.
|
||
- ✅ **TP14.1 (Session A) durch 2026-07-10:** alle ep14-Container auf vlan20 + pve-mcp neu; nur Omada-Inform-Adresse offen (Controller-UI-Task, s.u.).
|
||
- ✅ **TP14.2 (Session B) durch 2026-07-10:** pve-LXC (400/`docker`) auf VLAN20 (.20.126, ipvlan `parent=veth20`); omada-mcp/adguard→.20.x; CF-Connector jetzt VLAN20 = **#1033 gelöst**. Nächste Session = **TP14.3 (Session C, dormante Stacks + Referenz-Sweep ap1-DNS)**.
|
||
- ✅ **Netz-Abgleich 2026-07-11 (live via MikroMCP/pve-mcp):** **NAS `.20.10`, Mac mini `.20.167`, pve-LXC `.20.126`** alle reachable auf VLAN20 (+ Container `.20.x` populiert; `iobroker .107`/`ccu .232`/`z2m .106`/`vw .127` jetzt auch VLAN20). Sonst in VLAN1 (bridge) nur noch: sw1 `.11`/sw2 `.12`/ap2-Mgmt `.2` (Switch-/AP-Mgmt, interim bis MGMT-VLAN99) + Streu-Clients übers cudy-Alt-WLAN (MacBook `.182`, iPhone `.186`, SwitchBot `.184`, Sonoff) + die 16 VLAN1-Shellys (TP17). `defconf`/pool-v1-DHCP auf `bridge` läuft noch (VLAN1 aktiv).
|
||
- ✅ **pve-Host-Mgmt → VLAN20 (2026-07-13):** letzter verbliebener Host-Umzug. `vmbr0` bleibt VLAN1 `.41.100` (ohne Default-Route, interim), neues `vmbr0.20` (bereits vorbereitet, aber mit kaputter `address 10.40.20.0/24` vorgefunden) korrigiert auf `10.40.20.100/24` → `ifreload -a` live (ifupdown2, kein Reboot, LXC 400 durchgehend unbeeinflusst) → Default-GW auf `10.40.20.1` geflippt. SSH-Key-Auth bestätigt auf `.20.100`. Nachgezogen: ap1-DNS (`pve.bf.lan`→`.20.100`; dabei Fund+Fix eines Alt-Bugs: `pve-mcp.bf.lan` zeigte noch auf `.41.173` statt `.20.173`), CF-Tunnel `bf-pve` Ingress (`pve`+`j44d6tua`-SSH-Hostname .41.100→.20.100, PUT via `CF_API_TOKEN`, Smoke 200/200), CLAUDE.md, `docs/ip-plan.md`. `~/.claude.json` war schon sauber.
|
||
|
||
<a id="tp14-1"></a>
|
||
|
||
### TP14.1 — Session A: NAS fertigstellen ▸ ✅ DURCH (2026-07-10); Omada-Inform/sw2 VERTAGT
|
||
- ✅ **Alle ep14-Container auf vlan20 redeployed + verifiziert running/.20.x** (via Portainer-Stack-Redeploy, Env preserved, Volumes intakt): oc-stack(112 → prometheus `.151`/node-exporter `.154`/grafana `.109`), default(72 → hms-mqtt `.124`), nginx(101 → dashboard-web `.148`), oc-central(160 → qdrant `.159`), oc-charlie(158 → `.162`+cf), oc-johnny2(150 → `.161`+cf), gitea(111 → `.157`, DB `.20.23:3306` erreichbar ✓), drawio(107 → `.139`, intern; CF-CNAME war eh gelöscht).
|
||
- ✅ **pve-mcp NEU** als Stack (id 202, vlan20-only `.20.173`), verbunden mit Proxmox `.41.100` (v9.0.11) — ⚠️ **StreamableHTTP am Root-Pfad `/`, NICHT `/sse`** (mcp-pve v0.4.8, `MCP_TRANSPORT=http`). `~/.claude.json` `mcpServers.pve-mcp` NEU angelegt `{type:http, url:http://10.40.20.173:3000/}` (war vorher gar nicht drin) → wirkt nach Claude-Code-Restart.
|
||
- ✅ **Tunnel-Smoke** (CF-Ingress v116 zeigte schon auf `.20`): gitea 200, grafana 302, vw 200 — Origins existieren jetzt real.
|
||
- ⚠️ **alertmanager `.153`**: korrekt an vlan20, aber **crash-loop wegen PRE-EXISTING Config-Bug** (`${TELEGRAM_CHAT_ID}`-Literal in `/etc/alertmanager/alertmanager.yml` → int64-Parse-Fehler; Alertmanager macht kein Env-Substitution). NICHT migrationsbedingt, braucht echte chat_id. Out-of-scope.
|
||
- ⚠️ **openclaw-DB `10.40.41.122:3306` down** (shared MariaDB, pre-existing; Bots kamen trotzdem hoch). `DB_HOST`-Env unverändert gelassen. `OLLAMA_BASE_URL=.41.167`=Mac-mini bleibt korrekt `.41` (migriert nicht → **kein Blanket-Env-.41→.20!**).
|
||
- ⚪ **OBSOLET (2026-07-16) — ~~omada-controller Inform-Adresse + sw2 re-adopt~~:** ursprünglich vertagt (2026-07-10, Controller-Login schlug fehl). Gegenstandslos seit TP2.1: der Controller-Login-Ausfall eskalierte zum Werks-Reset von sw2, User hat daraufhin entschieden, sw2 **dauerhaft standalone ohne Controller** zu betreiben — und den Omada-Controller-Container samt omada-mcp am 2026-07-16 **komplett gelöscht** (overkill für 1 Switch auf der Abschussliste, s. TP15/CRS326). Kein Re-Adopt mehr vorgesehen.
|
||
- 🐞 **Gotcha gefunden:** Portainer-Stack 111 (gitea) hatte den `vlan20`-Networks-Block **auskommentiert** (Vorsession-Artefakt) → Container landete auf `gitea_default`/172.18. Fix = Redeploy mit Disk-File `docs/nas-vlan20/stacks/111-gitea.yml` als Override.
|
||
|
||
<a id="tp14-2"></a>
|
||
|
||
### TP14.2 — Session B: pve-Docker-LXC (.126) → VLAN20 ▸ ✅ DURCH (2026-07-10) (ipvlan)
|
||
- ✅ ep2-Container auf `vlan20`: **omada-mcp `.20.176`** (OMADA_BASE_URL→`.20.175`), **adguard_bf1 `.20.16`**, **adguard_fm2 `.20.17`** — alle running/reachable (health 200, adguard 302). Transf. Files `docs/nas-vlan20/stacks/{195-adguard,200-omada-mcp}.yml`.
|
||
- ✅ LXC 400 (`docker`) net0 → `tag=20, ip=10.40.20.126/24, gw=10.40.20.1` (⚠️ **Retag brauchte Christoph** — Token `claude@pam!claude` hat kein `VM.Config.Network`; UI-Edit, dann Reboot via API mit `VM.PowerMgmt`). Portainer wandert auf `https://10.40.20.126:9443`.
|
||
- 🐞 **Gotcha: in-Container-Iface heißt jetzt `veth20`, NICHT `eth0`** (UI setzte Name analog net1=veth16) → Docker `vlan20`=ipvlan **`parent=veth20`** (nicht eth0 wie im Runbook). cloudflared(host)/portainer(bridge) unberührt.
|
||
- ✅ **CF-Connector `db713b30` jetzt in VLAN20** (Tunnel `bf.emsenhuber.net` healthy/8 conns) = **löst TP11-Track-B/C** (#1033-Zielbild). Ingress `portainer` .41.126→.20.126. Smoke: portainer/gitea/vw 200, grafana 302.
|
||
- ✅ Refs nachgezogen: `mcp/portainer/.env` (PORTAINER_URL), `~/.claude.json` (omada-mcp `.20.176`), CLAUDE.md, `docs/ip-plan.md`, `docs/cf-tunnel-mapping.md`. Next = **TP14.3 (Session C, dormante Stacks + Referenz-Sweep ap1-DNS)**.
|
||
- ✅ **Live-Smoke re-verifiziert 2026-07-10:** alle 6 VLAN20-IPs ping OK + dienstseitig grün — Portainer `.20.126:9443` HTTP 200 (LXC 400 running, net0 `veth20 tag=20 gw/ns=.20.1`), omada-mcp `.20.176`→Controller `.20.175` v6.2.10.17 (HTTPS :8043 200), pve-mcp `.20.173` liefert Proxmox-Config, adguard `.20.16`/`.20.17` HTTP :80 302→login (nicht HTTPS/:3000).
|
||
|
||
<a id="tp14-2a"></a>
|
||
|
||
### TP14.2a — pve-mcp Single-Session-Fix + VLAN20-Host-DNS ▸ ✅ (2026-07-10)
|
||
- **pve-mcp „Failed to connect":** Ursache = **mcp-pve v0.4.8** kann im HTTP-Transport nur **eine** MCP-Session (ein globaler `StreamableHTTPServerTransport`, `sessionIdGenerator` gesetzt) → erster Client nach Boot belegt den Slot, jeder weitere Init (neue Claude-Code-Session, `claude mcp list`-Healthcheck, Reconnect) = `400 "Server already initialized"`. **omada-mcp NICHT betroffen** (frische Session-ID pro Init → Per-Session-Transport). **Fix = Upgrade auf 0.6.1** (Per-Session-Map, `serverFactory()` je Session, gleiche Env-Vars = Drop-in); Stack 202 neu deployed, Container `.20.173` = 0.6.1, `Connected to Proxmox VE 9.0.11`, `tools/list` liefert `pve_*`, **Claude Code ✔ Connected**. 3× init hintereinander = 3× 200 mit verschiedenen session-ids (verifiziert).
|
||
- **Kollateral-Fund — VLAN20-Host-DNS kaputt (beide Docker-Hosts):** nach dem VLAN20-Umzug zeigten **NAS** *und* **pve-LXC** `/etc/resolv.conf` noch auf totes `.41.254` → `docker pull` ghcr.io scheitert (`EAI_AGAIN` / 5s-Resolver-Timeout; Egress zu ghcr-IP `140.82.x:443` = OK 31 ms, also **nur Resolver tot**, nicht Egress). Fixes: **LXC** `.20.126` → `pct set 400 --nameserver 10.40.20.1` (Christoph). **NAS** → DSM-„Allgemein"-DNS war `.20.1`, ABER die **Netzwerkschnittstelle zog DNS per DHCP** und überschrieb `resolv.conf`; Fix = Schnittstelle „DNS manuell" `10.40.20.1` (Christoph) → NAS pullt wieder direkt. **Merkregel Synology:** GUI-DNS ≠ was `dockerd` liest (Quelle `/etc/resolv.conf`, per DHCP überschreibbar). **Workaround solange NAS-DNS tot:** `docker save`(LXC, hat DNS) → **gzip** (191-MB-tar reißt am Portainer-Agent-Socket-Proxy „context canceled"; ~70 MB gz geht) → Portainer `POST /endpoints/14/docker/images/load` → Stack-Redeploy `pullImage:false`. Gotcha: laufende Container frieren den `127.0.0.11`-Upstream zum Erstellzeitpunkt ein (Recreate nötig; für pve-mcp egal — Proxmox per IP `.41.100`). Memory `ref_pve_mcp_singlesession_and_vlan20_dns`.
|
||
|
||
<a id="tp14-3"></a>
|
||
|
||
### TP14.3 — Session C: Dormante Stacks vlan20-tauglich + Referenz-Sweep ▸ ✅ Referenz-Sweep durch (2026-07-16); dormante Stacks weiter bewusst nicht deployt
|
||
- ✅ **Dormante Stacks bereitgestellt (2026-07-11):** `transform.py` auf core(43)/openclaw(92)/openclaw2(97)/oc20260307(119)/openspeedtest(74)/speedtest_tracker(80)/technikscheiss(42) → **`.new.yml` + `.orig.yml`** in `docs/nas-vlan20/stacks/` (gitignored). **Nicht deployt** (User: „im Bedarfsfall später", Container-Zukunft ungewiss). 42/43 waren `default_internal` → jetzt vlan20-only (DHCP-.20.x). ⚠️ **Deploy-Caveats:** externe Refs bewusst NICHT umgeschrieben (`DB_HOST=.41.122` [pre-existing tote openclaw-DB, in 80+119], `OLLAMA_BASE_URL=.41.129`, `REDIS_URL=.41.111` in 119); **Kollision** `43-core` will `.20.23` = gitea-DB-IP (nicht gleichzeitig deploybar).
|
||
- ✅ **Referenz-Sweep `.41→.20` (2026-07-16, im Rahmen der TP19-Backup-Vorbereitung live gefunden + gefixt):** ap1 `/ip dns static` hatte noch `nas.bf.lan→.41.10` und `docker.bf.lan→.41.126` **stale** — korrigiert auf `.20.10`/`.20.126` (MikroMCP `manage_dns_entry`, dry-run+apply). Zusätzlich per User-Entscheid **Omada-Controller + omada-mcp komplett gelöscht** (overkill für 1 Switch auf der Abschussliste) → DNS-Einträge `omada.bf.lan`/`omada-mcp.bf.lan` entfernt statt migriert. `fritzbox.bf.lan` (totes Gerät, physisch weg seit 2026-07-01) ebenfalls entfernt. `docs/ip-plan.md` + CLAUDE.md nachgezogen.
|
||
- ✅ **ERLEDIGT/GEGENSTANDSLOS (live geprüft 2026-08-16):** ~~`~/.claude.json` (omada-mcp-Eintrag verwaist), CF-Ingress-Rest (`omada`/`omada-mcp`-Hostnames, ungeprüft)~~
|
||
- **`~/.claude.json`:** `omada-mcp` ist **nicht mehr als MCP-Server registriert** (`mcpServers` enthält ihn nicht). Er steht nur noch in `disabledMcpServers` einiger Projekt-Scopes — ein reiner Merkposten „bewusst aus", keine Verbindungsversuche, kein Handlungsbedarf.
|
||
- **CF-Ingress:** alle 9 Tunnel durchgesehen (`cfd_tunnel` + `/configurations`) — **kein einziger `omada`-Hostname mehr**. Sauber.
|
||
- ↪️ **Shelly-Geräte-Übersiedelung ausgegliedert → TP17** (die ~26 IoT-Shellys sind eine eigenständige WLAN-Migration, nicht Teil des Docker-Ref-Sweeps).
|
||
|
||
<a id="tp14-5"></a>
|
||
|
||
### TP14.5 — Session E: Freigabe-Gate → TP5-Firewall ▸ ✅ ERFÜLLT (2026-08-08) — kein eigener Change
|
||
- Gate „TP14.1–14.3 durch (alle Origins/Connectoren in VLAN20)" ist erfüllt → **TP5 (Deny-by-default) ist entblockt.** Reines Reihenfolge-Gate, kein eigener Change.
|
||
|
||
<a id="tp15"></a>
|
||
|
||
> 📦 **Nachtrag 2026-08-31:** die vier Skripte, die den Cutover begleitet haben
|
||
> (`apply-core-cutover-l3`, `backup-core`, `restore-core`, `backup-ap1`), liegen jetzt unter
|
||
> [`routeros/archiv-tp15-cutover/`](../routeros/archiv-tp15-cutover/README.md). Sie zeigten alle
|
||
> auf tote Adressen — `backup-ap1.py` allerdings auf eine **lebende falsche** (`10.40.10.1` ist die
|
||
> VLAN10-SVI des Core, nicht ap1) und hätte still das falsche Gerät gesichert.
|
||
>
|
||
### TP15 — HW-Refresh: Switching + Router ▸ ✅ **ABGESCHLOSSEN (2026-08-08)** — Bring-up beider CRS326 + Branch-Tausch (2026-07-23) **und** Core-Tausch/L3-Umzug (2026-08-08, Durchführung s. TP21.1) sind durch — `docs/hw-refresh-requirements.md`, Memory `project_mikrotik_commit_nautobot_gc`
|
||
> 🔌 **Scope-Änderung 2026-08-16 (User-Entscheid): die DC-USV ist aus diesem TP herausgelöst und heißt jetzt **TP24**.** Sie war der letzte offene Rest hier, hat mit Switching/Routing sachlich nichts zu tun und trägt eigene Entscheidungen (Elektrik, Batterie, Standort). TP15 ist damit inhaltlich zu.
|
||
> *Vorherige Header-Fassung (Stand 2026-08-05, überholt): „🟢 CRS326-Bring-up + Branch-Tausch DURCH (2026-07-23); Core-Tausch/L3-Umzug **vorbereitet, Cutover-Config materialisiert (2026-08-05)**, Cutover = nächstes Wartungsfenster."*
|
||
- ✅ **BRING-UP BEIDER CRS326 + BRANCH-TAUSCH gegen sw2 — DURCH (2026-07-23, Claude via REST unter Governance-Ausnahme #2).** Ablauf/As-built:
|
||
- **Zugriffsweg (Henne-Ei gelöst):** Erst-Reihenfolge war **core zuerst** (User-Korrektur — core hängt an sw1 und ist der L2-Pfad, über den branch überhaupt erreichbar wird; branch war isoliert). Zugriff auf die Werks-`192.168.88.1` über **temporäre ap1-Zweit-IP `192.168.88.2/24` + srcnat-Masquerade `192.168.88.0/24`** (beide `TP15-temp`, nach Bring-up wieder entfernt — Rückweg nötig, weil die Werks-Box keine Route zu VLAN10 hat). Config dann komplett per **REST** (`/system/script` PUT+run), kein Winbox-Paste. **Winbox-MAC-Connect scheiterte reproduzierbar** („macconnection sync timeout" über WLAN/quer durch den Fabric — braucht selben Draht).
|
||
- **Identitäten am Gerät verifiziert (nicht nur Doku):** `core` = sw-id **4W04-URHT**, MAC `04:F4:1C:B4:17:76`, Serial `HKA0AREXJFM`, PW `X9…`; `branch` = sw-id **RGZJ-UK0T**, MAC `04:F4:1C:B4:15:58`, Serial `HKA0AJJA52F`, PW `PHR…` (ap2 hängt an branch **ether24** — bridge-host bestätigt = definitiv branch). **PWs in `~/.secrets/credentials.env`.**
|
||
- **branch = produktiv, volle SOLL-Portbelegung** (User-manuell erhoben, maßgeblich): ether1 Uplink-Trunk→sw1 #8 (native VLAN1 + tagged 10/20/30/40/50/90/99, **kein 100**), ether2+ether8 = **NAS 2×tagged-VLAN20** (zwei unabhängige Links), ether3 Drucker/VLAN10, ether4 Dock/VLAN10, ether5+ether6 Sonos/VLAN40, ether7 Mac mini/VLAN20, ether24 ap2-Trunk; ether9–23+sfp disabled. vlan-filtering=yes. Mgmt **`10.40.99.13` (VLAN99)** erreichbar über sw1 #8 (+ VLAN1-Temp `.41.213` während Bring-up). **Post-Swap-Abnahme grün:** NAS `.20.10`, Mac mini `.20.167` (VLAN20), Dock `.10.194` (VLAN10), Sonos `.40.191`/`.40.192` (VLAN40).
|
||
- **core = grundkonfiguriert, NICHT produktiv** (bleibt an sw1 #12): identity/vlan-filtering/volle VLAN-Tabelle, ether1 Uplink, ether2 Temp-Downlink (war Config-Pfad für branch), Mgmt **`10.40.99.11`** (VLAN99, erreichbar erst wenn sw1 #12 99T trägt) + VLAN1-Temp `.41.211`. Port-Layout ab ether3 tentativ (final in Core-Swap-Session).
|
||
- **Beide in MikroMCP `routers.yaml`** (crs326-core/-branch, ENV `ROUTER_CRS326_*` in `~/.claude.json` — **live erst nach MCP-Neustart**, diese Session lief alles per REST-curl). **Nautobot** modelliert + GraphQL-verifiziert (`nautobot/ingest-crs326-ports.py`): branch Active + volle Ports + primary_ip4 `.99.13`; core Planned + tentatives Layout; **sw1 verifizierte 16-Port-Belegung** (löst „UNVERIFIZIERT"); **sw2 → Decommissioning**.
|
||
- 🐞 **Gotchas festgehalten:** (1) **Kabel ether3↔ether5 waren beim Umstecken vertauscht** → ein AZ-Sonos landete als Access-VLAN10 in `10.40.10.190` **und** blutete via SonosNet nach VLAN40 (= Dual-Homing-Muster, `project_dual_homing_slowness_incident`) → Kabeltausch gefixt, beide AZ-Sonos jetzt sauber VLAN40 (`.191`/`.192` bound). (2) RouterOS-Script-`policy`-Feld per REST wählerisch → **weglassen** (admin-Default = volle Policy). (3) Disable-Regex mit `^…$`-Ankern griff nicht → `[find name!="ether1" and …]` nutzen. (4) REST-`PUT`-Antwort mit mehrzeiligem `source` ist kein sauberes JSON → ID separat via `?.proplist=.id` holen. (5) Claude-Code-Auto-Mode-Classifier blockt Device-Config-*Writes* per Bash (Reads ok) → `autoMode.allow`-Regel in `.claude/settings.local.json` nötig (kann Claude sich nicht selbst setzen).
|
||
- ✅ **ERLEDIGT 2026-08-08 (Durchführung + Recovery s. TP21.1).** ~~🔜 Als Nächstes (eigene Session, Übergabe): Core-Tausch = L3-Umzug~~ (Routing/DHCP/DNS/CF-Mesh von ap1 → crs326-core) — **Vollausfall-Fenster**, s. `docs/tp15-core-swap-session-handoff-prompt.md`. Danach WLAN-Neuprovisionierung (TP3, CAPsMAN-Abbau). ~~Finaler Mgmt-Renumber dann: branch `.99.13`→`.99.12`, core bleibt `.99.11`.~~ ⚠️ **Anders gekommen (Ist seit 2026-08-08, live bestätigt): core `.99.1` · branch `.99.2` · ap1 `.99.11` · ap2 `.99.12`.**
|
||
- ✅ **Core-Swap VORBEREITET (2026-07-23, reine Prep-Session, kein Netz-Eingriff):** ap1s Live-L3-Config vollständig gelesen (MikroMCP) und als **staged Config** materialisiert → `routeros/tp15-core-l3-staged.rsc` (alles Betriebsumlenkende `disabled=yes`, im Cutover in einem Rutsch `enable`; ~50 Reservierungen + WG-Privkey bewusst erst im Fenster maschinell von ap1 geholt statt abgetippt). Runbook/Rollback/Verifikation im Plan. **3 Fenster-Start-Entscheide festgelegt:** (1) **WG-Hub (TP10 FM/DW-VPN) migriert MIT** — war im Handoff-Prompt übersehen, hängt an ap1s Public-Edge und kann dort nicht bleiben; (2) Governance = **frische Direkt-Ausnahme #3** (REST/MikroMCP), danach intended→Nautobot; (3) Rettungspfad = **iPhone-Hotspot**.
|
||
- ✅ **Nautobot-Modell + Golden-Config-Template für L3-Cutover vervollständigt (2026-08-05):** `crs326-core` jetzt Rolle *Router*, alle VLAN-SVIs (`vlan10/20/30/40/50/90/99/100`) + Gateway-IPs (`.1`) modelliert; `nautobot/mikrotik_routeros.j2` (Gitea `bf-golden-config`) erzeugt für `crs326-core` die vollständige staged L3-Konfig (Interface-Lists, GW-IPs, WAN-DHCP, DHCP-Server/Pools, DNS, NAT, Firewall, WG-Hub) und für alle anderen MikroTik-Geräte nur die Identität. Git-Sync + Generate Intended Config erfolgreich getestet; intended config für `crs326-core` liegt in Nautobot vor.
|
||
- ✅ **ap1-Cutover-Daten exportiert + in Cutover-Config eingebaut (2026-08-05):** 54 statische DHCP-Leases aus ap1 (`dhcp-v10`/`v20`/`v30`/`v40`) nach `routeros/ap1-leases-to-core.rsc` überführt; WG-Hub-Privkey von ap1 geholt und in die lokale Cutover-Config `routeros/tp15-core-cutover-intended.rsc` eingesetzt (der **WG-Privkey landet NICHT im Git-Template** — Secret).
|
||
- 🐞 **Prep-Gotchas:** (a) **MikroMCP kann core noch nicht schreiben** — `ROUTER_CRS326_CORE_*` im MCP-Prozess nicht geladen (jeder Zugriff „Missing environment variable") → vor dem Fenster MCP neu starten oder REST-curl gegen `http://10.40.41.211/rest`. (b) Mgmt-`.99.11` tot (sw1 #12 trägt kein 99T), nur VLAN1-Temp `.41.211` erreichbar. (c) **WG-Hub:** Privkey von ap1 mitnehmen (sonst FM/DW-Reconfig nötig). (d) core-vlan99 wird GW `.1`; Bring-up-`.11` dadurch redundant (Fenster-Reconcile). (e) VLAN1 raus: Hybrid-VLAN1-Drops sw1 GE3/7/10 beim Umstecken einem echten VLAN zuordnen oder stilllegen.
|
||
- ✅ **WG-Endpoint-Frage geklärt (2026-07-23):** Spokes adressieren den Hub über `bf4.emsenhuber.net:51821` = CF-A-Record (proxied=false, TTL 120, aktuell `90.146.35.38`). **Kein Updater auf ap1** → nichts zu migrieren; `bf4` folgt der IP manuell (offener TP10-Follow-up). Zieht core am WAN eine neue IP (Lease sticky, eher nicht), via `CF_API_TOKEN` nachziehen: `bf4`-Record + Access-Policy `bf4-home-ip` (`90.146.35.38/32`). **Chance:** core ist nicht `device-mode=home` → scheduler/fetch gehen → `bf4`-DDNS-Auto-Updater dort bauen (schließt TP10-Follow-up).
|
||
- 📋 **Session-Handover** für die Cutover-Session: `docs/tp15-core-swap-session-handover.md` (Einstieg + Pre-Window-Checkliste + Kurz-Runbook; Original-Auftrag bleibt `docs/tp15-core-swap-session-handoff-prompt.md`).
|
||
- 📦 **2× CRS326-24G-2S+RM werden morgen (2026-07-17) geliefert, Produktivsetzung spätestens Montag 2026-07-20 (User-Entscheid 2026-07-16).** Sequenzierung ggü. Nautobot geklärt: **TP20+TP21 werden bis dahin fast-getrackt** (TP18/Komodo als Blocker entfernt, s. TP20) — Ziel ist, dass Nautobot **vor** der CRS326-Produktivsetzung steht und die Switches **direkt als Nautobot-verwaltete Geräte adoptiert** werden, statt erst von Hand konfiguriert und später importiert. sw1/sw2-VLAN99-Umzug (TP19.1) läuft parallel dazu on-demand, unabhängig davon.
|
||
- **🧭 Grundsatzentscheid (2026-07-14, korrigiert 2026-07-15): reines MikroTik-Ecosystem — Routing + Switching + WLAN.** **Omada bekommt KEINE Rolle** (weder Switching noch WLAN — der ursprüngliche „Omada=WLAN"-Satz war ein Fehlschluss beim Dokumentieren, User-Korrektur 2026-07-15: „WLAN war NIE von Omada die Rede"). Begründung Switching: Projektziel #1 **CAKE** nur RouterOS/OpenWrt/OPNsense (Omada-Gateway kann kein CAKE) → Switching = MikroTik CRS. WLAN bleibt auf hAP ax² (~~/CAPsMAN~~ — **CAPsMAN am 2026-08-08 abgeschafft**, Standalone aus der Golden Config; die Hardware-Aussage gilt), kein EAP-Kauf, Coverage-Fix bleibt Platzierungsfrage (TP3.3). Verworfene Vollvarianten (Omada-only, MikroTik-only) samt Begründung + Ansible/Nautobot-Recherche → Memory. **Anlass der Korrektur:** Omada-Controller↔sw2-Link beim VLAN20-Umzug (TP14.1) zerbrochen, versehentlicher Werks-Reset bei Rettungsversuch, fehlerhafter Wiederherstellungsversuch → sw2 wird standalone neu eingerichtet, s. **TP2.1**.
|
||
- **🛒 Switches: 2× CRS326-24G-2S+RM bestellt (2026-07-14 — CSS326-Fehlbestellung storniert → CRS).** 24×1G + 2×SFP+, lüfterlos, DC-input (USV-tauglich). Lösen ALLNET-Core (sw1) + Omada-ES208G (sw2, s. TP2.1 — Solo/Interim bis CRS-Tausch) + Cudy ab.
|
||
- ✅ **CRS = RouterOS-fähig → in-model:** MikroMCP-Write + Nautobot Golden Config (TP21) managen auch die **Switches**, nicht nur den Router → config-as-code deckt den **ganzen Kabel-Fabric**. (SwOS-Boot bliebe als Fallback möglich, aber nicht genutzt.)
|
||
- ⚠️ **kein PoE** (Sonoff/PoE-Verbraucher via Injektor, falls nötig).
|
||
- **📶 WLAN bleibt hAP ax²** — kein Hardware-/Ecosystem-Wechsel; die Hardware-Aussage gilt unverändert.
|
||
⚠️ **Die Betriebsart hat zweimal gewechselt, letzter Stand ist CAPsMAN:** CAPsMAN abgeschafft
|
||
(2026-07-22/08-08, TP3-Rebuild → Standalone-APs) → **wieder eingeführt am 2026-08-17 (TP28), Manager
|
||
ist `crs326-core`, ap1/ap2 sind reine CAPs.** Wer hier nur bis zum ersten Pfeil liest, baut auf einem
|
||
überholten Stand auf. Coverage-Schwachstellen (P750W-Fall, Terrasse/Kanten) bleiben Platzierungs-/Roaming-Thema von **TP3.3**, nicht durch neue Hardware gelöst.
|
||
- **🧭 Router-HW final (User-Entscheid 2026-07-22): kein RB5009.** Kein Zukauf einer dedizierten Router-HW.
|
||
- **🔄 KORREKTUR (User-Entscheid 2026-07-22, später am selben Tag): der CRS326-Core WIRD der neue L3-Router.** Die frühere Fassung dieser Zeile („Router bleibt dauerhaft ap1/ap2, L3/CAKE/Firewall bleiben auf ap1") ist damit **überholt**. **Routing, DNS und DHCP wandern vollständig von ap1 auf `crs326-core`**; ap1/ap2 werden zu **reinen Access Points**. Auch der CF-Mesh-Endpoint terminiert dort (s. TP16.1, war schon vorher auf CRS326 retargetet — passt jetzt konsistent zusammen). Damit entfallen die `device-mode=home`-Einschränkungen des hAP (kein scheduler/fetch) für die Router-Rolle. **Reihenfolge (User): erst Switches aufsetzen und in Betrieb nehmen, danach WLAN-Neuprovisionierung der APs.** Betroffen/nachzuziehen: TP4 (Router-Rolle), TP5/TP6 (Firewall + CAKE landen auf dem CRS326), TP21 (Scope-Grenze).
|
||
- **📍 CRS326 Live-Stand (2026-07-22, via MNDP von ap1):** **beide Geräte sind bereits am Netz und laufen** — RouterOS **7.19.5**, Werkszustand. `crs326-core` = MAC `04:F4:1C:B4:17:76` (software-id `4W04-URHT`), `crs326-branch` = MAC `04:F4:1C:B4:15:58` (`RGZJ-UK0T`). ⚠️ **Beide auf Werks-IP `192.168.88.1` im selben L2-Segment = IP-Konflikt** → beim Bring-up nur eines nach dem anderen adressieren, Unterscheidung ausschließlich per MAC. Die frühere Notiz „Uplink-Port bislang ungenutzt/down" ist überholt. **Standorte: `crs326-core` = „Technik UG" (virtueller Technikbereich im UG/Diele, KEIN Keller), `crs326-branch` = „Technik OG".**
|
||
- **🔌 PoE-Anschluss der APs (User-Vorgabe 2026-07-22):** Verkabelung/Portbelegung bleibt sonst **unverändert**; ap1/ap2 sollen künftig aber am **PoE-Port** hängen. PoE selbst ist noch kein Thema, die Config soll es aber schon vorsehen.
|
||
- ✅ **Port = `ether1`.** RouterOS nummeriert Interfaces ab **1** (`ether1`…`ether5`), nie `ether0` — die Unix-Konvention `eth0` gilt hier nicht. Auf dem hAP ax² ist `ether1` der PoE-Port (live bestätigt: `/interface/ethernet/poe` listet ausschließlich `ether1`).
|
||
- ⚠️ **Aber: passives PoE 18–28 V, NICHT 802.3af/at.** Ein normaler 802.3af/at-Switch oder -Injektor speist den hAP ax² **nicht**. Es braucht einen **passiven PoE-Injektor** (MikroTik-Standard). Dazu passend: **der CRS326 hat gar kein PoE** (bereits dokumentiert) → Strom kommt ohnehin aus einem Inline-Injektor zwischen Switchport und AP, nicht aus dem Switch.
|
||
- ⚠️ **Config-Konsequenz:** ap1s Trunk liegt heute auf **`ether2`** → wandert bei der Neuprovisionierung auf **`ether1`**. Und `ether1` steht aktuell auf **`poe-out=auto-on`** (der hAP kann über denselben Port auch Strom *abgeben*) — beim Umbau auf „ether1 = versorgter Uplink" muss `poe-out` auf diesem Port **`off`** gesetzt werden, sonst speisen Injektor und AP gegeneinander.
|
||
- **🔋 DC-USV → ausgelagert nach **TP24** (2026-08-16).** Inhalt unverändert gültig (MeanWell LRS-150-48 + DDR-120-48 + LiFePO4 20 Ah, ~€190, 48-V-Bus; die CRS326 haben passenden DC-Input) — steht ab jetzt dort, samt der offenen Entscheidungen (Mac mini/Inverter, Bus-Standort nach dem Core-Umzug, Batteriegröße, Shutdown-Signalisierung).
|
||
- ✅ **Standort-/Ablösungs-Mapping final (User 2026-07-22) — sauberes 1:1:**
|
||
| Bereich | heute | wird abgelöst durch |
|
||
|---|---|---|
|
||
| **Technik UG** (virtueller Bereich in der Diele, **kein Keller**) | sw1 (ALLNET, 16 Ports) | **crs326-core** (= neuer L3-Router) |
|
||
| **Technik OG** | sw2 (ES208G, 8 Ports) + ap2 + cudy2 | **crs326-branch** |
|
||
| **Wohnzimmer Sideboard** | ap1 | — (bleibt, wird reiner AP) |
|
||
Kapazität unkritisch: 16+8 belegte Ports heute gegen 2× 24 GbE + 2× SFP+ künftig. Vollständig in Nautobot gepflegt (alle 8 Devices haben einen Bereich).
|
||
- **📻 cudy-Hardware (User 2026-07-22):** beide sind **Cudy WR3600E V1.0**, Firmware `2.3.15-20250905-191956`. `cudy1` = Technik UG, am Core `#11`, aktiv als Übergangs-WLAN (`elektrosmog.c`, `10.40.10.246`) — **und Christophs aktueller WLAN-Pfad**. `cudy2` = Technik OG, außer Betrieb (Nautobot-Status `Decommissioning`), wird nicht reaktiviert.
|
||
- ⚠️ **Etappe 1 (Erstzugriff auf die CRS326) ist NICHT kritisch** (User 2026-07-22): Werkszustand, Gerät steht griffbereit neben ihm → der IP-Konflikt auf `192.168.88.1` wird physisch/seriell aufgelöst, kein Lockout-Szenario.
|
||
- ✅ **Portplan-Widerspruch AUFGELÖST (User 2026-07-22): am Core ist `#15 = ap1` und `#11 = cudy1`.** Damit gilt die TP4-Variante (`gi16` Arris/VLAN100 → `gi15` Trunk → ap1 `ether2`); **`switch/cli/03-trunk-ap-ports.cli` (gi1 = ap1 / gi2 = ap2) ist STALE** — die Datei bezeichnet sich selbst als „provisorisch, 2026-06-09" und wurde vom Edge-Cutover am 2026-07-01 überholt. Nicht mehr als Quelle verwenden.
|
||
- **Zielport nach dem Tausch: ap1 wandert auf `crs326-core` Port 4** (User-Vorgabe). Gleichzeitig wandert ap1s Uplink geräteseitig von `ether2` auf `ether1` (PoE-Port, s.o.).
|
||
- ⚠️ **Nach wie vor NICHT vom Gerät gelesen:** die restliche sw1-Belegung. `switch/state-backups/sw1-running-config-20260716-*.txt` sind 214-Byte-Fehlschläge (`Incorrect User Name!!`, toter `claudecode`-Login), `switch/cli/running-PRE.txt` ist Pre-TP1. `gi16` (Arris) und `gi8` (Trunk → sw2) stehen in Nautobot daher **explizit als „UNVERIFIZIERT"** markiert.
|
||
- 🔴 **Betriebliches Kernrisiko (Analyse 2026-07-22): der Core-Tausch ist KEIN Switch-Tausch, sondern ein Vollausfall.** `crs326-core` ersetzt sw1 **und** wird gleichzeitig L3-Router — und an sw1 hängt auf `#15` ap1, also Gateway, DHCP, DNS und der WAN-Trunk fürs gesamte Netz. Core-Tausch und L3-Umzug sind damit **faktisch dasselbe Ereignis** und brauchen ein geplantes Ausfallfenster. **→ Reihenfolge-Entscheid (User 2026-07-22): `crs326-branch` zuerst (gegen sw2, Technik OG), `crs326-core` in einer eigenen, späteren Session.**
|
||
- 🛟 **Rettungspfade liegen günstig auf verschiedenen Switches:** Christophs Notebook hängt per **WLAN über cudy1 → Core `#11`**; der kabelgebundene Plan B (Dock/Monitor Arbeitszimmer) hängt am **sw2/Branch (P4)**. Beim **Branch**-Tausch stirbt also der Kabelweg, das WLAN überlebt. (Beim Core-Tausch stirbt beides — s. Kernrisiko oben.)
|
||
- **Hinweis:** die A/B/C/D-Switch-Varianten in `hw-refresh-requirements.md` sind durch die CRS326-Beschaffung überholt (Doc = Recherche-Archiv + USV/Energie-Referenz).
|
||
- Abh.: TP14 ✅ (IP-Migration). Verzahnt: **TP20** (Platform-DB), **TP21** (Nautobot GC), **TP2.1** (sw2-Standalone-Recovery).
|
||
|
||
<a id="tp16-1"></a>
|
||
|
||
### TP16.1 — Private Access via native Cloudflare Mesh (WireGuard) auf `crs326-core` ▸ ✅ **ABGESCHLOSSEN (2026-08-17)** — `docs/tp16.1-warp-mesh-native-crs326.md`
|
||
- ✅ **Kernergebnis: ICMP ist repariert.** Privater Zugang unterwegs läuft über **natives WireGuard auf `crs326-core`** statt cloudflared-QUIC — **0 % Ping-Loss auf alle BF-VLANs, vorher 100 %**. Genau das Symptom aus dem Incident 2026-07-16, das dieses TP ausgelöst hat. Öffentliche FQDNs unberührt auf dem 3-Tunnel-HA (TP16). Compliance **68/68**.
|
||
- ✅ **§4.0 erledigt 2026-08-17: Mesh-Node angelegt.** `bf-core-mesh` = **`d1da7c8a-2736-4cbb-8af1-32db5393fb9c`**, Register-Token in `mcp/cloudflare/.env` (`CF_MESH_REGISTER_TOKEN`, gitignored). ➡️ **Die seit 2026-07-17 offene Free-Plan-Frage ist beantwortet: der Free-ZT-Plan erlaubt Cloudflare Mesh — der LXC-Fallback ist vom Tisch.**
|
||
- 🔑 **Der Weg dahin war ein Token-Problem, kein Plan-Problem (kostete 2 Anläufe).** Der alte User-Token hatte **zwei** fehlende Permissions (§4.0 *und* §4.1 — `devices/settings` war schon lesend `403`). Ersetzt durch einen **account-owned** Token (`9984078812bc…`, ⏰ **gültig bis 2027-01-09**). Zwei Fallen, beide sehen aus wie „Token kaputt": (1) Account-Tokens verifizieren an `/accounts/{id}/tokens/verify` — am alten `/user/tokens/verify` melden sie `1000 "Invalid API Token"`, **während alle echten Endpoints 200 liefern**; (2) Dashboard-Fehler `"Account tag in access policy must match tag in request uri"` = eine **Zone**-Permission (`DNS`) steckt in der Policy-Zeile mit **Account**-Ressourcen → braucht eine eigene Zeile. Verifiziertes Soll-Set + Permission-IDs in `docs/tp16.1-…md` §4.0. **Merke: Dashboard sagt „Edit", API sagt „Write".**
|
||
- ✅ **§4.1–§4.4 durchgezogen 2026-08-17 (User-Freigabe „jetzt durchziehen").** Prereqs: `use_zt_virtual_ip=true`, Device-Profil „Cloudflare Mesh" (`4d3c0f57…`), Split-Tunnel auf **beiden** Profilen korrigiert (`100.64.0.0/10` → `/11` + `100.112.0.0/12`). Gerät: `cf-mesh` auf `crs326-core`, Mesh-IP **`100.96.0.6/32`**, Peer `162.159.193.2:2408`, **Handshake steht**, Route `100.96.0.0/12 → cf-mesh` aktiv; Backup `pre-tp16.1-mesh-20260817.backup` vorher (Auflage #4 ✅). CF meldet Node **healthy**. **§4.4: Weg A hat funktioniert — der befürchtete vnet-Gotcha trat NICHT ein**, `10.40.0.0/16` zeigt jetzt auf `bf-core-mesh` statt `bf-pve` (Rollback = 1 PATCH zurück, `bf-pve` unangetastet).
|
||
- ⚠️ **Zwei Werkzeug-Fallen, real getroffen (gehören in jede künftige WG-Arbeit):** (1) **MikroMCPs `manage_wireguard_interface` kann grundsätzlich keinen privaten Schlüssel annehmen** — RouterOS erzeugt ihn selbst; bei CF Mesh kommt er aber von CF → Key per SSH mit Interpolation aus der gitignoreten `.env` setzen, nicht über das Tool. (2) **`manage_wireguard_peer` schreibt `endpoint: "IP:Port"` komplett in `endpoint-address` und lässt `endpoint-port=0`** → **kein Handshake**, sieht aus wie ein Firewall-Problem, ist ein Parameter-Bug. Nach jedem Peer-Add `print detail` gegenlesen. Auch `persistent-keepalive` fehlt dem Tool.
|
||
- ✅ **§4.5 ABNAHME BESTANDEN 2026-08-17** (MacBook am Hotspot, WARP connected): **Ping 0 % Loss** auf `10.40.10.1` / `10.40.20.105` / `10.40.99.12` / `10.40.20.10` / `10.40.99.1` (47–63 ms) — **vorher 100 %**. TCP mit echtem Datenfluss (pve 8006 → 200, Nautobot → 302, `ssh macmini` → ok). FQDN-Smoke gitea/hass/pve/nautobot/vw → 200/200/302/302/200 ⇒ **cloudflared-Public-Teil unberührt.**
|
||
- ✅ **Auflage Ausnahme #4 erfüllt:** Ist-Stand als intended config zurück nach Nautobot → zuerst **65/68** (drei `extra`-Drifts = die Mesh-Zeilen), Zeilen **byte-genau aus dem Backup** ins Jinja-Template, Push nach `bf-golden-config`, Repo-Sync → **68/68, keine Drifts**. Privkey bleibt draußen (`<<REDACTED>>`-Maske wie bei `wg-hub`). ⚠️ **Push-Gotcha:** Nautobot schreibt Backups+intended in **dasselbe** Repo → `git pull --rebase` vor jedem Push, sonst `rejected (fetch first)`.
|
||
- ✅ **Mgmt-ACL via GC ausgerollt 2026-08-17 (Nachlauf, alle 4 Geräte) — `72/72 compliant`.** Neue Compliance-Feature **`ip-service`** (`match_config=/ip service`, `config_ordered=false`) → 17→18 Regeln, 68→**72 Checks**. Sollzustand einheitlich im Template: `ssh`/`www`/`winbox`/`api` = `address=10.40.0.0/16,100.96.0.0/12`, `ftp`/`telnet`/`api-ssl` = `disabled=yes`. Ablauf regelkonform: Backups auf allen Zielen → `--job mgmt-acl --apply --no-deploy` → **gestaffelter** Deploy (ap2 zuerst, am Gerät gegengeprüft, dann ap1/branch/core) → Geräte-Gegenprobe → Compliance.
|
||
- 🔎 **Der Befund war das Gegenteil der Erwartung:** ich hatte „branch/ap1/ap2 sperren den Mesh aus" befürchtet — tatsächlich hatten sie **gar keine** Address-Beschränkung (ssh/www/winbox/api/ftp/telnet **komplett offen**), und die dynamischen Einträge zeigten `REMOTE 100.96.0.1`, waren also längst per Mesh erreichbar. **`winbox` war auf ALLEN vier unbeschränkt**, auch am Core. Aus dem Erreichbarkeits-Fix wurde damit eine Härtung.
|
||
- ⚠️ **Scope-Erweiterung, bewusst und meldepflichtig:** `ftp`/`telnet`/`api-ssl` wurden auf branch/ap1/ap2 **abgeschaltet** (Klartext bzw. ungenutzt; der Core hatte das bereits). Begründung: die Alternative wäre gewesen, „telnet aktiv" als Sollzustand in die SoT zu schreiben. Nichts hier nutzt telnet/ftp — der ALLNET-Telnet-Runner ist Archiv, das Gerät ausgebaut.
|
||
- 📌 **Schreibweise (Falle):** `/ip service set ssh …` mit dem **Dienstnamen**, NICHT `[find name=ssh]` — letzteres trifft auch den dynamischen Verbindungs-Eintrag und quittiert mit `failure: this is configured elsewhere`.
|
||
- 🔴🔴 **DIE FALLE DIESES TPs — `/ip service` sperrte das Router-Mgmt aus dem Mesh aus** (Core, vor dem Rollout oben): Direkt nach dem WARP-Connect war `crs326-core` mgmt-tot: MikroMCP `Connection lost before handshake`, SSH `kex_exchange_identification: Connection reset`, REST **502** — **bei gleichzeitig verlustfreiem Ping auf dieselbe IP**, und alle Hosts *hinter* dem Core normal. Ursache: `/ip service ssh,www address=10.40.0.0/16`, der Mesh-Client kommt aus `100.96.0.0/12`. ICMP kennt keine Service-ACL. **Und `nc -z` meldet „offen", weil der CF-Gateway-Proxy den TCP-Handshake terminiert — ein TCP-Connect ist über WARP KEIN Reachability-Beweis.** Fix: `address=10.40.0.0/16,100.96.0.0/12`. **Merkregel: wer per Mesh ans Netz kommt, ist noch lange nicht am Router.** ⚠️ **Für `crs326-branch`/`ap1`/`ap2` noch OFFEN** — deren eigene `/ip service`-ACLs sind ungeprüft. Entscheidender Diagnose-Schritt war der **Banner-Grab vom macmini** (`nc 10.40.99.1 22` → `SSH-2.0-ROSSSH`): von innen gesund ⇒ Pfad/ACL, nicht Gerät. Fehlspuren, die man sich sparen kann: MTU (die großen Pings scheitern am **WARP-Client**-`utun` MTU 1380, nicht an `cf-mesh`) und SSH-Session-Erschöpfung.
|
||
- ✅ **Schalter „Allow all Cloudflare One traffic to reach enrolled devices" — recherchiert 2026-08-17, ENTSCHIEDEN: bleibt AUS.** Laut CF-Settings-Referenz ist es ein **globales** Device-Client-Setting („Global device client settings"), alle Pläne, alle OS, Client-Mode „Traffic and DNS". **Funktion: es lässt Traffic aus Mesh/WAN *zu den eingebuchten Geräten hin* — also Zugriff auf Device-IPs.** Das ist die **Client↔Client**-Richtung und für unseren Fall (Gerät → Mesh-Node → `10.40.0.0/16`) nicht nötig; der Mesh läuft bewiesenermaßen ohne. **Kein API-Feld** (in Spec, `devices/settings`-PATCH *und* Profil-GET gegengeprüft), und die CF-Docs nennen **keinen** Menüpfad. ⚠️ **Die Docs widersprechen sich:** die Mesh-Get-Started-Seite nennt es eine *Profil*-Einstellung, die Settings-Referenz eine *globale*. Nach User-Vorgabe („im Zweifel ohne Schalter") **nicht gesetzt** — nichts gebaut, nichts geklickt. Erst relevant, wenn ein Zugriff **auf ein Endgerät** (statt auf das Netz) gebraucht wird.
|
||
- 📌 **Bleibt gültig:** die HA-Degradierung der öffentlichen Tunnel (TP16, `bf-pve` fährt nur einen Connector) wird davon **nicht** geheilt — eigener offener Punkt. Und für **TP5** vormerken: bei deny-by-default braucht der Mesh explizit `forward accept 100.96.0.0/12 ↔ 10.40.0.0/16`.
|
||
- ✅ **Gate erfüllt (2026-08-16):** die Bedingung „gated bis TP21 den CRS326 abdeckt" ist eingelöst — `crs326-core` ist seit 2026-08-08 der produktive L3-Router und seit 2026-08-13 in Nautobot adoptiert (68/68 compliant). Runbook §3 neu gemessen, §4.3 von Platzhalter auf **echte Werte** gezogen.
|
||
- 🧭 **Erwartung geraderücken (wichtig, weil leicht zu verwechseln): das Mesh ersetzt die CF-Tunnel NICHT.** TP16.1 löst ausschließlich den **privaten** Zugang (unterwegs → `10.40.0.0/16` per IP, **inklusive ICMP**) und tauscht dort den cloudflared-QUIC-Proxy gegen natives WireGuard auf dem Core. Die **öffentlichen FQDNs bleiben** auf dem 3-Tunnel-HA aus TP16. Beide Stränge laufen nebeneinander — die HA-Degradierung aus TP16 (2026-08-16) wird davon **nicht** geheilt und bleibt ein eigener offener Punkt.
|
||
- 📌 **Live-Befunde 2026-08-16 (read-only erhoben, kein Eingriff):**
|
||
- `warp_connector` = `[]`, aber der **GET antwortet** (kein 403) → die Free-Plan-Frage entscheidet sich erst am `POST` (§4.0). `CF_API_TOKEN` ist `active`; ob er die separate Permission **„Cloudflare One Connector: WARP"** trägt, zeigt genau dieser POST — dort schlug es am 2026-07-17 mit `10000` fehl.
|
||
- `use_zt_virtual_ip: false` → Unique-Device-IP muss weiterhin eingeschaltet werden. Gateway-Proxy TCP+UDP sind an.
|
||
- **Nur EIN Device-Profil** (Default). Der „auf beiden Profilen"-Hinweis im Runbook meint also: Default jetzt, Mesh-Profil sobald es existiert — es gibt kein zweites, auf dem man etwas übersehen könnte.
|
||
- Split-Tunnel-Exclude enthält unverändert `100.64.0.0/10` (verschluckt die Mesh-Overlay-Range) → der §4.1.4-Schritt bleibt nötig.
|
||
- Die vnet-Route `10.40.0.0/16` (`5546dcb6…`) zeigt weiter auf `bf-pve` — der Fallback steht. ⚠️ **Aber `bf-pve` fährt derzeit nur einen Connector**, der private Pfad hängt also an einem einzelnen cloudflared-Prozess.
|
||
- ✅ **Die letzte inhaltliche Unbekannte ist weg:** „Rückweg-Routing TBD" hat sich erledigt, weil der Core L3-Gateway **aller** VLANs ist — es braucht keine Zusatzroute. Und `wg-hub` (TP10) liegt auf **demselben** Gerät, ist also direktes Vorbild statt fernes Muster.
|
||
- ✅ **GOVERNANCE-AUSNAHME #4 (User, 2026-08-16): die WG-Zeilen für den Mesh-Bring-up auf `crs326-core` dürfen DIREKT gesetzt werden** (MikroMCP `plan_changes` dryRun → `apply_plan`). **Begründung — es ist kein Bequemlichkeits-, sondern ein struktureller Grund:** TP22-Regel 1 schließt jede Sektion, die `<<REDACTED>>` enthält, generell vom GC-Push aus, und `/interface wireguard` ist genau so eine Sektion. Ein privater Schlüssel ist über Golden Config also **gar nicht deploybar** — die frühere Zeile „CRS326-Zeilen laufen regulär über Nautobot GC" trägt nicht mehr. **Auflage (wie Ausnahme #2):** Ist-Stand danach als intended config zurück nach Nautobot; vorher `/system backup` (Regel 3). **Scope-Grenze:** nur der Mesh-Bring-up auf `crs326-core` — nicht spätere WG-Änderungen, nicht andere Geräte.
|
||
- 🔑 **Damit die Ausnahme ein einmaliger Bootstrap bleibt — Secret-Handling-Design (Runbook §4.3):** ein privater Schlüssel ist **Geräte-Identität**, keine Konfiguration (wie ein SSH-Hostkey). Regel: *der private Teil entsteht am Gerät und verlässt es nie; nur der öffentliche geht in die SoT und wird in die **Peer**-Configs gerendert.* RouterOS kann das nativ (`/interface wireguard add` erzeugt den Privkey selbst). Compliance bleibt grün, weil die `ConfigReplace`-Maske aus TP22 Schritt 0 byte-identisch zu der im Template ist. ⚠️ Vorbehalt: bei CF Mesh erzeugt **CF** das Schlüsselpaar — dann fällt die On-Device-Erzeugung weg, das Prinzip („nie in Git, nie in der intended config") hält trotzdem. **Stufe 2** (`nautobot-secrets-providers` + Vault, Wert erst bei der Plan-Erzeugung einsetzen) lohnt bei 4 Geräten und einem Key nicht — eigener TP nach TP18/TP20.1.
|
||
- 🧹 **Sofort fälliger Modellierungs-Nachzug, unabhängig vom Mesh:** die WG-Peers `fm`/`dw` stehen mit hartkodierten `public-key="…"` im Jinja-Template. **Public Keys sind Daten, keine Templatezeilen** — gehören nach Nautobot und von dort gerendert.
|
||
- ⚠️ **Für TP5 vormerken:** sobald deny-by-default greift, braucht der Mesh-Verkehr explizit `forward accept 100.96.0.0/12 ↔ 10.40.0.0/16` — heute passiert er nur, weil die `forward`-Kette ohne Drop endet.
|
||
- ⏭️ **Was die Scharfschalt-Session macht:** `POST warp_connector` (Plan-Gate) → account-weite Prereqs (Unique Device IP, Split-Tunnel auf beiden Profilen) → `wgcf-connector`-Token → 4 WG-Zeilen auf dem Core → Route `5546dcb6…` umhängen (vnet-Gotcha, Weg A/B) → Ping-Abnahme. **Trifft alle Live-WARP-Geräte mit einem kurzen Reconnect** → Zeitpunkt user-gated.
|
||
- ✅ **Update (User-Entscheid 2026-07-22):** Ziel-Device für den WG-Mesh-Node ist jetzt der **CRS326 Core-Switch statt ap1** — konsistent mit dem TP15-Endstand (kein RB5009) — ⚠️ **die damalige Klammer „Router bleibt ap1/ap2 rein für L3/WLAN" ist seit dem Core-Cutover 2026-08-08 überholt: L3 liegt auf `crs326-core`, ap1/ap2 sind reine APs.** Der Retarget auf den Core war damit im Ergebnis richtig, nur anders begründet als hier notiert und TP21s "ganzer Kabel-Fabric via RouterOS"-Modell (CRS326 ist ohnehin RouterOS-fähig und wird Nautobot-verwaltet). **Reine Ziel-Doku-Änderung** — das Scharfschalten bleibt weiterhin eine eigene, spätere Session und bleibt gated, bis TP21 den CRS326 als Device abdeckt. **Keine neue Governance-Ausnahme:** die bestehende ap1-Ausnahme (s. Ist-Stand-Zeile 31) gilt explizit **nicht** automatisch für den CRS326 — CRS326-Konfiguration soll regulär über Nautobot GC laufen, sobald TP21 so weit ist. Die CRS326 selbst sind aktuell noch Werkszustand (physisch bereits mit dem aktuellen Core verkabelt, aber die betreffende Uplink-Port bislang ungenutzt/down) — konkrete Management-IP/Interface-Zuordnung für den CRS326 als L3/WG-Endpoint ist noch **nicht** getroffen (TBD nach TP15-Bring-up).
|
||
- **Trennung:** Public FQDNs bleiben auf cloudflared-3-Tunnel-HA (TP16); **Private Access** (unterwegs → `10.40.0.0/16` per IP inkl. ICMP) wandert vom cloudflared-QUIC-Proxy auf einen **nativen WireGuard-Mesh-Node auf dem CRS326 Core-Switch**.
|
||
- **Warum CRS326-nativ:** Cloudflare Mesh spricht WireGuard, RouterOS kann natives WireGuard (TP10-Hub auf ap1 *ist* einer, Muster überträgt sich) → Mesh-Node = WG-Peer zu CF-PoP, **kein Container/TUN/LXC/neue HW**. Verlagert die Rolle vom Router (ap1, der weiterhin Public-Edge + WLAN-Anker bleibt) auf den Core-Switch, der als zentraler Kabel-Fabric-Knoten ohnehin alle VLANs sieht. RouterOS-Container-Weg bleibt tot (kein `/dev/net/tun`-Passthrough auf JEDER Router/Switch-HW) — daher weiterhin natives WireGuard, kein Container-Ansatz.
|
||
- **Verworfen:** WG-Roadwarrior (User will keine Extra-App/Profil je Gerät), alle Helper-Host-ZT-Anbieter (SPOF-Verletzung).
|
||
- **Warum heute nur Konzept:** TCP-Pfad funktioniert (nur ICMP-Komfort fehlt), und die CF-Prerequisites sind **account-weit** (Unique-Device-IP, Split-Tunnel-Exclude, Device-Profile) → treffen Live-WARP-Geräte (1× kurzer Reconnect) → nicht unter Zeitdruck. Zusätzlich jetzt gated durch den Retarget: CRS326-Bring-up (Management-IP, VLAN-Filtering) muss erst stehen.
|
||
- **Nächste Session (nach CRS326-Bring-up):** Plan-Probe `POST warp_connector` (Free-Plan-Gate; sonst LXC-Fallback) → account-weite Prereqs → `wgcf-connector`-Token→WG-Config → 4 WG-Zeilen auf dem CRS326 → Subnetz-Route + vnet-Route `5546dcb6…` von bf-pve umhängen (Rollback trivial) → Verifikation (Ping verlustfrei). Team-Domain `emsenhuber.cloudflareaccess.com`. Governance „Config nur via Nautobot" → CRS326-Zeilen regulär via TP21/GC, sobald das Device dort adoptiert ist.
|
||
- ✅ **Recherche-Härtung 2026-07-17 (Primärquellen animmouse + offizielle CF-Docs + OpenAPI-Spec, alles verifiziert, gilt unabhängig vom Zielgerät):** (1) Produkt heißt seit 2026-04-14 offiziell **Cloudflare Mesh** (API-Pfade bleiben `warp_connector`, im Spec bestätigt inkl. Token- und sogar HA-Endpoints); (2) ICMP-Proxy ist **bereits AN** (User-Korrektur: fm-Ping `10.101.0.0/24` beweist es — kein To-Do; mein „fehlt" war ein Fehlschluss aus dem API-Spec, das Flag ist Dashboard-only); (3) Split-Tunnel-Exclude auf **beiden** Device-Profilen (Default + Mesh); (4) **vnet-Gotcha** beim Route-Umzug („virtual networks only for cloudflared") → Weg A PATCH, Weg B DELETE+POST dokumentiert; (5) **v4-only-Entscheid** (Mesh-IPv6-site-to-site laut Guide broken, NAT66-Gefrickel nicht nötig); (6) keepalive 1m statt 25s ok. Alles in `docs/tp16.1-warp-mesh-native-crs326.md` §2/§4 eingearbeitet (Config-Block dort als Platzhalter markiert, IP/Interface TBD).
|
||
- Abh.: TP16 ✅ (Public-Teil), TP10 ✅ (WG-Grundmuster, jetzt als Vorbild statt Direktziel), **TP15** (CRS326-Bring-up), **TP21** (Nautobot-Adoption des CRS326, Voraussetzung fürs Scharfschalten). SSoT: `docs/tp16.1-warp-mesh-native-crs326.md`, `docs/cf-tunnel-mapping.md §14`.
|
||
|
||
<a id="tp17"></a>
|
||
|
||
### TP17 — Shelly-Übersiedelung VLAN1 → VLAN30 (IOT) + `shelly-mcp` ▸ ✅ **KOMPLETT (User-Bestätigt 2026-08-13): alle Shellys auf VLAN30** — Geräte-PW *(war TP17.2, seit 2026-08-24 **TP30** Posten #3)* — `mcp/shelly/`, Runbook `docs/tp17-shelly-go-runbook.md`
|
||
- ✅ **Abschluss 2026-08-13 (User-Bestätigt):** auch die zuletzt offenen saisonalen Shellys (s1/s2/s3/s12) sind inzwischen umgeklemmt — **alle Shellys auf `dirtyvader-iot`/VLAN30**, kein VLAN1-Shelly-Rest mehr. Migration-Teil von TP17 damit vollständig abgeschlossen.
|
||
- ✅ **GO durchgezogen 2026-07-13 (16 Shellys VLAN1→VLAN30, 0 Fehler):** Pilot s26 + Welle A (s4–s9) + Welle B (s10/11/13/15/17/19/20/21/25) → alle auf `dirtyvader-iot`/`10.40.30.<octet>` verifiziert (SSID+IP+rssi). Mit den 6 Vorab-Migrierten (s14/16/18/22/23/24) = **22/26 auf VLAN30**; VLAN1-Sweep `.180-250` = **0 Shellys**. Restliche 4 saisonal ausgesteckte (s1/s2/s3/s12) inzwischen ebenfalls migriert (s.o.).
|
||
- 🔧 **Anker-Verfahren (User-Idee, statt sta1-Backup):** je Gerät **vor** dem sta-Write die geräteeigene **AP-Funktion** aktiviert (`WiFi.SetConfig ap.enable` — Firmware verlangt WPA2, kein offenes AP → PSK=IOT-PSK, live/kein Reboot) = Notanker, falls VLAN30-Join scheitert (Gerät bleibt über `Shelly*-<mac>`-AP erreichbar, **kein physischer Reset**). Nach Verify alle 22 AP-Anker wieder `deanchor`t. Runner: `mcp/shelly/migrate.py` (`migrate`/`deanchor`/`auth`/`table`, Dry-Run default, stoppt bei erstem Fehler, reused `discover.py`).
|
||
- ✅ **HA-Cross-Check nach Batch:** einzige `unavailable` Shelly-Entities = **s12** (saisonal aus) — alle 22 migrierten reconnecten via mDNS/zeroconf (TP7-Repeater vlan30↔vlan20-HA) auf neuer `.30.x`-IP, **Entity-IDs unverändert** (keyt per MAC). Inventar `inventory/bf.csv` .41→.30 nachgezogen (22 Zeilen, Section→IOT).
|
||
- ⚠️ **Coverage-Flag:** `s4 kueche_mikrowelle` verbindet mit **rssi −93** (Metallgehäuse), hat aber verifiziert — grenzwertig, im Auge behalten. s7 Tiefkühl −85. Rest gut (−26…−81).
|
||
- ✅ **Eco-Mode-Policy (2026-07-13, PROVISORISCH):** eco senkt CPU-Takt + WiFi-TX-Leistung → **eco AN für RSSI ≥ −80** (Energie-Win, TX irrelevant), **eco AUS für < −80** (Uplink-Headroom): s4/−91, s7/−88, s17/−81, s18/−79…−82, s19/−86 → 17× ON / 5× OFF, live via `Sys.SetConfig device.eco_mode` (kein Reboot). s4 (Küchen-Sockel/Metall, −91, fix) bewusst eco-OFF = schwächster Link. ⏳ **2. Eval nötig nach TP3.3** (WLAN-Ausrichtung/Aufbau ändert RSSI → Grenzfälle neu bewerten; User 2026-07-13).
|
||
- ✅ **HA-Referenz http vs https geklärt (2026-07-13):** HA speichert `configuration_url` für **alle 22 Shellys bereits `http://10.40.30.x:80`** (Integration korrekt + neue IPs übernommen). Das „https" ist **client-seitig** (Mixed-Content-Upgrade, wenn HA selbst über https/Tunnel geöffnet) — **kein HA-Fix möglich/nötig, Shelly-SSL bewusst NICHT** (CPU/Energie, IoT-Netz). ⚠️ Rand-Fund: 1 HA-Shelly-Device zeigt auf `10.95.10.206` (Fremd-Subnetz, nicht BF) → separat prüfen.
|
||
- ⏳ **Rest (nicht mehr Migration, s. TP17.2/TP19):** (1) einheitliches Geräte-PW → **zurückgestellt als [[#TP17.2]]** (Kosmetik). (2) Nicht-Shelly-Mover + cudy-Teardown + VLAN1-Client-Retirement → läuft in **TP19**. (3) DNS/ip-plan Restfeinschliff (mit TP19).
|
||
|
||
- **Ziel:** ~26 Shellys (aktuell untagged VLAN1 `10.40.41.2xx`, weil auf **alt-WLAN cudy**, s. TP3.1/`project_vlan1_dhcp_incident`) auf SSID **`dirtyvader-iot`** (VLAN30, `w2-iot`, WPA2/WPA3-mixed, PSK `74c9L283$669`) umklemmen → DHCP `dhcp-v30`/pool-v30 (live). **Kein netzseitiger Move möglich** (anderes WLAN) → per-Gerät WiFi-Reconfig.
|
||
- **User-Entscheide (2026-07-11):** (1) **volles `shelly-mcp`** bauen (reusable, nicht nur Skript); (2) **Pilot 2–3, dann Rest**; (3) **DHCP-Reservierungen je MAC** in VLAN30 (stabile IPs für HA/ioBroker/DNS).
|
||
- ✅ **Recon durch (`mcp/shelly/discover.py`, read-only /shelly-Sweep):** 16 live, **alle Gen3/Gen4** (uniforme RPC-API `WiFi.SetConfig`, auth=off — kein Gen1-Sonderfall). 10 „offline" = **weihnachten_\*** (s1/s2/s3, saisonal ausgesteckt Juli) + s12/s14/s16/s18/s22/s23 (aus/DHCP-drift). ⚠️ **Inventar-Fix:** s6 (kueche_kuehlschrank, `.213`) MAC falsch erfasst (`0c:72:74:36:90:9e`) → real **`d0:cf:13:d8:18:fc`**. s24 hat gar keine MAC.
|
||
- **Mechanismus je Gerät:** Gen2+ RPC `WiFi.SetConfig {sta:{ssid,pass,enable}}` → Reboot → landet `.30.x`. ⚠️ **Risiken:** falsches PSK → AP-Mode (physischer Reset); Gerät kurz offline; HA/ioBroker-Refs brechen bis re-pointed; WPA3-mixed ggf. zickig auf alter FW; ap1 2,4-GHz-Master `wifi2` `running:false` → Coverage v.a. via ap2 prüfen.
|
||
- ✅ **Live-Abgleich 2026-07-11 (go-ready):** (a) `shelly-mcp` (`server.py`) fertig — discover/get/set_sta/verify/reservation_plan, PSK in `.env` gesetzt (noch nicht als MCP registriert, optional). (b) **Alle VLAN30-DHCP-Reservierungen liegen** (`.30.2xx`, Oktett erhalten, außerhalb Pool `.100–.200`); **s6-Reservierung `.30.213` MAC gefixt** (falsch `0C:72…9E` → real `D0:CF:13:D8:18:FC`). (c) **IOT-SSID live verifiziert:** ap1 `w2-iot` `running:true`, 8 Clients −26…−68 dBm (ap1-`wifi2`-„running:false" ist kosmetisch). (d) **Migration bereits ~5/26 durch:** s14/16/18/22/23 seit ~2,5 d stabil auf reservierten `.30.221/.223/.225/.202/.203` (+ `.30.204` unbekannt, evtl. s24) → **Pilot faktisch bestanden**.
|
||
- ✅ **HA-Cross-Check 2026-07-11 (alle IPv4-Geräte in HA):** Shellys deckungsgleich; **`.30.204` = Shelly „Bad/Spiegelschrank" (Plus RGBW PM, `14:33:5c:d8:2d:7c`) = vmtl. s24** → 6 statt 5 schon migriert. **Netzweite Aufstellung + VLAN1-Leerstand-Analyse** im Runbook: nach TP17 + pve-Host→VLAN20 + Christophs Endgeräte→VLAN10 ist VLAN1 **endgeräte-frei**; es bleiben nur Switch-/AP-Mgmt (sw1/sw2/ap2 → VLAN99-Cutover, eigener Schritt) + **1 unidentifiziertes Gerät `.41.246`** (morgen klären). **Nicht-Shelly-VLAN30-Mover (User-bestätigt):** ✅ SwitchBot floor-lamp `.41.184` + ✅ Twinkly-Girlande `.30.228` (beide 2026-07-16 übersiedelt, s. TP19) + Creality Ender (fehlt noch, off). **Withings schon übersiedelt; Tasmota existiert nicht mehr** (kein aktives Gerät → stale HA-Eintrag). EOS-Smartwatches/eBook-Reader später + von Christoph selbst (eBook eher VLAN50).
|
||
- **Scope-Erweiterung (User 2026-07-11):** je Gerät **3 Änderungen** — WLAN→`dirtyvader-iot` + IP (`.41.2xx`→`.30.2xx`) **+ einheitliches Geräte-Passwort** (`Shelly.SetAuth`, User `admin`, realm-individueller ha1 → Tool `set_auth` in `server.py` nachrüsten). **PW entschieden: eigenes Shelly-Admin-PW** (getrennt vom WLAN-PSK) → `SHELLY_DEVICE_PW` in `.env` (Christoph liefert Wert). **HA-Entities unverändert** (keyt per MAC). **HA-Entities bleiben unverändert** (HA keyt per MAC). **Projektion TP17.1 (experimentell, nachrangig):** Gen4 (~11 St.) auf **Thread/Matter** flashen (Firmware-Verfügbarkeit + Thread-Border-Router offen) — Konflikt mit „Entities unverändert" (Matter = neue Entities) → erst WiFi-Migration stabilisieren.
|
||
- ✅ **Reservierungs-/Pool-Konvention (User 2026-07-11):** alle Shellys bekommen **statische IP** (`10.40.30.<octet>`, gw/dns `.30.1`) **+ in-pool DHCP-Reservierung** als Backup. `pool-v30` auf `.100-.250` erweitert (Reservierungen jetzt in-pool); `set_sta` auf `static=true` umgestellt; s24-Inventar + `.30.204`-Reservierung angelegt. **Vollständiger HA-MAC-Scan:** 2 verpasste Media-Geräte **Apple TV 4K + Philips TV** (aus, → VLAN40) — **vor cudy-Abschuss** VLAN verifizieren; **Apple TV = vmtl. Thread-Border-Router** (TP17.1). **`.41.246/.247 = cudy1/cudy2`** (Teardown-Ziele). Tasmota bestätigt tot.
|
||
- **Offene Schritte (morgen, „nur noch go", Runbook `docs/tp17-shelly-go-runbook.md`):** die **16 verbliebenen VLAN1-Shellys** (s4–s11, s13, s15, s17, s19–s21, s25, s26 auf `10.40.41.2xx`) per `WiFi.SetConfig`→`dirtyvader-iot` umklemmen (Batch, je Gerät `.30.x` verifizieren) → Geräte-PW setzen → HA/ioBroker-Re-Point → SwitchBot/Tasmota/3. Nicht-Shelly → saisonale s1/s2/s3/s12 beim nächsten Einstecken → cudy-Alt-WLAN abbauen → DNS/Inventar/`ip-plan` nachziehen.
|
||
- Abh.: SSID/VLAN30 (TP3/TP4 ✅), DHCP-v30 ✅. Verwandt: `project_vlan1_dhcp_incident` (cudy-Rest-Leak), TP5 (IOT-Firewall danach). Memory `project_shelly_migration_tp17`.
|
||
- ➡️ **Nachtrag 2026-08-23: die Projektion „TP17.1 — Gen4 auf Thread/Matter" ist keine Projektion
|
||
mehr.** Christoph hat sie aktiviert; sie steht als **offenes TP17.1** in
|
||
[`teilprojekte.md`](teilprojekte.md), die Umsetzung liegt im HA-Projekt
|
||
(`~/dev/ha-xstate/docs/shelly-gen4-thread-migration.md`). Der damals notierte Vorbehalt
|
||
„Firmware-Verfügbarkeit + Thread-Border-Router offen" hat sich **halb** aufgelöst: der Border
|
||
Router läuft (~30 Matter-Knoten), die **Firmware gibt es von Shelly aber bis heute nicht** —
|
||
offiziell ist Gen4 nur Matter *over WiFi*, Thread nur über Dritt-Firmware. Auch der zweite
|
||
Vorbehalt gilt unverändert: **Matter = neue Entities**, der Konflikt mit „HA-Entities unverändert"
|
||
ist real und in der Übergabe ausgeführt.
|
||
- 📊 **Bestandszahl, live 2026-08-23:** 22 der 26 Shellys erreichbar — **12 Gen4**, 8 Gen3, 2 Gen2.
|
||
Die „~11 Gen4" aus der Schätzung von 2026-07-11 waren also nah dran.
|
||
- 🔑 **Nachtrag 2026-08-23 — der IOT-PSK steht in der „Ziel"-Zeile oben im KLARTEXT, und das bleibt
|
||
vorerst so (User-Entscheid).** Der Wert ist am selben Tag gegen den Manager geprüft worden und ist
|
||
**noch der live konfigurierte**. Bewusst **nicht** redigiert und **keine** History-Rewrite:
|
||
- Redigieren allein hilft nicht — die Zeile liegt seit `e76289c` (2026-07-11) in der Historie.
|
||
- Ein `git filter-repo` wäre hier Aufwand ohne Gegenwert: **eine** getrackte Fundstelle, drei
|
||
Commits, Remote **nur** `gitea.emsenhuber.net` (kein GitHub), **ein** lokaler Klon — der Wert hat
|
||
die eigene Infrastruktur nie verlassen.
|
||
- **Der wirksame Schritt ist die Rotation**, und die kostet heute ~26 Geräte. Sie ist deshalb
|
||
bewusst **hinter TP17.1** eingereiht (Gen4 → Thread), s. Follow-ups in
|
||
[`teilprojekte.md`](teilprojekte.md).
|
||
⚠️ **Wer diesen Abschnitt liest, behandelt den Wert als aktives Geheimnis** — nicht zitieren, nicht
|
||
in Transkripte, nicht in Tickets. Zum Abgleich reicht ein Fingerprint (Länge + Stichproben),
|
||
genau so ist er am 08-23 verifiziert worden.
|
||
|
||
<a id="tp19"></a>
|
||
|
||
### TP19 — cudy-Teardown + VLAN1-Client-Retirement ▸ ✅ **GESCHLOSSEN (2026-08-18)** — `docs/tp19-vlan1-cudy-teardown-runbook.md`
|
||
- ✅ **Abschluss-Sweep 2026-08-18, alles live gegengeprueft (reine Reads):**
|
||
- **VLAN1 ist leer:** `/ip arp` auf `bridge` = **0 Eintraege, 0 reachable**. Nicht Restverkehr,
|
||
sondern gar nichts. Der Dual-DHCP-Race aus `project_vlan1_dhcp_incident` hat damit endgueltig
|
||
keine Quelle mehr.
|
||
- **Prometheus:** kein einziges `10.40.41.x`-Ziel mehr. Aktiv sind `localhost:9090`, n8n
|
||
(`.20.167:5678`), grafana (`.20.109:3000`); die uebrigen acht Jobs sind auskommentiert.
|
||
Der in der alten Restliste genannte Punkt ist damit **verifiziert** erledigt, nicht nur behauptet.
|
||
- **Interim-Kruecken sind weg:** `/interface bridge port` auf ap2 hat **keinen** Port mit
|
||
eingeschraenkten `frame-types` mehr — die TP3.1-Haertung `admit-only-vlan-tagged` ist beim
|
||
Rebuild verschwunden. Nichts abzuraeumen. `st-main` ist seit TP3.3 fester Templatebestandteil.
|
||
- **DNS-Statics** zeigen ausschliesslich auf `.99.x`/`.20.x`, kein `.41.x`.
|
||
- **cudy1:** `core ether11` ohne Link seit 2026-08-08 19:33, in Nautobot *Offline*. cudy2 tot.
|
||
- 🔜 **Was NICHT mehr TP19 ist** (bewusst dorthin verschoben, wo es hingehoert, statt den TP offen zu halten):
|
||
- **Der physische Ausbau von cudy1** — Hands-on-Aufgabe, blockiert nichts, kein Netzeffekt.
|
||
- **Die 4 toten CF-Tunnel** (`coolify`, `openclaw` …) — das sind Karteileichen in der
|
||
Cloudflare-Config, kein Netzthema → **TP16**.
|
||
- **HA-Portainer `setup_retry`** (`docker system df` auf ep14 braucht 33–120 s, struktureller
|
||
Plattformnachteil) → Monitoring-Thema, **TP11**. Aussichtsreichster Weg bleibt ein eigener
|
||
Portainer-Benutzer ohne Zugriff auf ep14.
|
||
- 📌 **Nebenbefund beim Abschluss, sofort behoben: der Standort hatte keine funktionierende Zeitquelle.**
|
||
ap1/ap2 zeigten per NTP auf `10.40.99.1`, wo **kein** Server lief (`status=waiting`), branch hatte
|
||
gar keinen eingetragen. **ap2 war dadurch 17 Stunden versetzt** — aufgefallen, weil ein Logeintrag
|
||
von 04:57 als `2026-08-17 12:19` gestempelt wurde. Der Core ist jetzt NTP-Server
|
||
(`routeros/tp19-ntp-core-als-zeitquelle.rsc`, inkl. der noetigen `input`-Regel — ohne die waere der
|
||
Server seit Etappe 4 eingeschaltet und trotzdem unerreichbar gewesen), branch ist Client
|
||
(`routeros/tp19-ntp-branch-client.rsc`).
|
||
- 🔎 **Die Wurzel lag eine Ebene tiefer:** auch der Core hatte **keinen** NTP-Client
|
||
(`enabled: no`, keine Server) bei `use-local-clock: no`. Ein Server ohne eigene Synchronisation
|
||
liefert Stratum 16, das jeder Client verwirft — die Anfragen kamen also an (die Firewall-Regel
|
||
zaehlte) und wurden trotzdem nicht brauchbar beantwortet. Der Standort hatte damit **an keiner
|
||
Stelle** eine funktionierende Zeitsynchronisation; dass drei von vier Uhren stimmten, war
|
||
Restglueck vom letzten manuellen Stellen. Behoben mit `routeros/tp19-ntp-core-client.rsc`
|
||
(`time.cloudflare.com,at.pool.ntp.org`) → Core `synchronized`.
|
||
- 🔴 **Und der letzte Schritt war der unauffaelligste: der Server hielt Stratum 16 fest.** Er war
|
||
eingeschaltet worden, **waehrend sein eigener Client noch unsynchronisiert war** — RouterOS
|
||
bewertet das nicht von selbst neu. Die Anfragen kamen an (die Firewall-Regel zaehlte mit),
|
||
wurden aber unbrauchbar beantwortet, und alle drei Clients blieben auf `waiting`.
|
||
**Ein `enabled=no` / `enabled=yes` nach der Synchronisation hat es geloest.**
|
||
Der entscheidende Test dorthin: branch einmal auf `at.pool.ntp.org` zeigen lassen — es
|
||
synchronisierte in 20 Sekunden. Damit war bewiesen, dass der Client heil ist und der Server
|
||
schweigt, statt weiter im Nebel zu stochern.
|
||
✅ **Endstand: alle vier Geraete `synchronized`.** (ap2s Uhr musste einmalig von Hand gestellt
|
||
werden — 17 h liegen ueber der NTP-Panic-Schwelle, so weit springt kein Client von selbst.)
|
||
- ✅ **Governance nachgezogen (2026-08-18):** NTP steht jetzt im **GC-Template** (Core als Quelle
|
||
mit oeffentlichem Upstream, branch/APs als Clients auf `10.40.99.1`), wird per **DHCP-Option 42**
|
||
(`ntp-server=` je Netz, Christophs Vorschlag) an **alle** Clients ausgeliefert — nicht nur an die
|
||
MikroTik-Flotte — und ist mit der neuen ComplianceRule **`system-ntp`** drift-ueberwacht.
|
||
Das war exakt der blinde Fleck, den **TP26** fuer `/ip firewall filter` geschlossen hat.
|
||
- ✅ **Live-Nachprüfung 2026-08-16 (MikroMCP + CF-API, reine Reads):**
|
||
- **`cudy1` ist tot, nicht nur „offline gemeldet":** `core ether11` steht auf `running=false`, **letzter Link-Down 2026-08-08 19:33**, seither kein Link. Die Formulierung „aktiver VLAN1-Leak" ist damit endgültig hinfällig — der **Dual-DHCP-Race hat keine Quelle mehr**. Offen bleibt nur der *physische* Ausbau + Inventar/Doku.
|
||
- **`crs326-core` hat keine VLAN1-IP mehr:** `/ip/address` listet nur noch die 7 SVIs + die WAN-DHCP-Adresse — die **Bring-up-Altlast `10.40.41.211` ist weg**. Damit fällt einer der 5 Compliance-Drifts aus TP22 raus, *sobald* wieder **Backup → Intended → Compliance** gelaufen ist (die Compliance vergleicht gegen das letzte Backup, nicht gegen das Gerät — s. TP22-Falle).
|
||
- ✅ **`crs326-branch`-Interim ERLEDIGT (2026-08-16):** `10.40.10.2` + SVI `vlan10` über einen GC-ConfigPlan entfernt (s. TP22). Mgmt läuft über `.99.2`; `/interface bridge vlan` blieb byte-identisch, es gibt also weder einen Traffic- noch einen Drift-Effekt.
|
||
- **`.41.133` identifiziert** (war TP19 B1.2 „unidentifiziertes VLAN1-Gerät"): es ist das **coolify-Origin** aus dem gleichnamigen, seit Monaten toten CF-Tunnel. Ebenso `.41.147/.148/.139` aus dem toten `openclaw`-Tunnel. Keine aktiven Geräte — **Karteileichen in der Cloudflare-Config**, nicht im Netz. Aufräumen = die 4 toten Tunnel löschen (user-gated, s. TP16-Nebenbefund).
|
||
- ✅ **VLAN1-Adressen abgeräumt (2026-08-15) — der Weg dorthin** — `docs/vlan1-rueckbau-howto-manuelle-schritte.md`:
|
||
- **pve `10.40.41.100` weg** (`/etc/network/interfaces`: `vmbr0` von `inet static` auf `inet manual`, Adresszeile raus; angewendet per `ip addr del` **ohne** `ifreload -a`, damit die Bridge nicht durchgetreten wird). Nachher nur noch `vmbr0.20`, keine `41.0/24`-Route, alle 9 Gäste weiter `running`, Storage aktiv. Backup `/root/interfaces.bak-tp19-20260815-183045`.
|
||
- **Core `10.40.41.211` weg** über Nautobot GC (`routeros/tp19-core-vlan1-address-remove.rsc`). Mgmt `.99.1` + `10.40.10.1` intakt. Compliance damals 17/17 grün — der TP22-Backlog-Punkt „`crs326-core 10.40.41.211`" entfällt. *(Zahl inzwischen überholt: nach TP16.1 sind es 72/72 Checks.)*
|
||
- **HA zeigt auf `10.40.20.100`** (User), **ioBroker** korrigiert (User).
|
||
- **Prometheus bereinigt.** Der pauschale `41`→`20`-Ersatz war eine Falle: nur `grafana` (`.20.109`) und `n8n` (`.20.167:5678`) liefern echte Metriken. `gitea` hat `/metrics` gar nicht aktiviert, `vaultwarden` gibt 404, `portainer /api/status` liefert JSON statt Prometheus-Format, `node`/`alertmanager`/`qdrant` sind exited, `pihole` ist abgewählt → **8 Jobs + `alerting:`-Block auskommentiert**, 3 aktive Ziele, alle `up`.
|
||
- 🔴 **NAS-Falle:** `prometheus.yml` gehört `65534:65534`, das Verzeichnis `adm.christoph` → `cat > datei` scheitert still („Permission denied"), während ein `cp` daneben klappt. **Schreiben per `sudo tee` + `md5sum`-Gegenprobe**, nie der Shell-Erfolgsmeldung trauen.
|
||
- ⏳ **OFFEN: HA-Portainer-Integration bleibt auf `setup_retry` — `docker system df` auf nas-bf ist zu langsam.** Aus dem HA-Debug-Log: `PortainerTimeoutError … GET /api/endpoints/14/docker/system/df` → `ConfigEntryNotReady`. Die Integration ruft `system df` für **jede** Umgebung bei **jedem** Update auf.
|
||
- **Container-Manager-Neustart am 2026-08-15 durchgeführt** (`/usr/syno/bin/synopkg stop|start ContainerManager` — Binary **nicht** im sudo-PATH; DSM meldet den Status danach weiterhin falsch als `stop`). **Alle 14 Container aus der Baseline zurück**, zusätzlich kam `hms-mqtt` hoch (Restart-Policy `always`, lief vorher nicht).
|
||
- ✅ **`hms-mqtt` stillgelegt (2026-08-16, User: „wird bald platt gemacht").** Es war bereits beendet; entscheidend war die **Restart-Policy `always` → `no`** (`docker update --restart=no`), sonst wäre es beim nächsten Daemon-Neustart wiedergekommen. Ein bloßes `docker stop` hätte hier nicht gehalten.
|
||
- ✅ Der **Hänger** ist damit weg: `system df` beendet sich wieder (vorher rc=124 nach Ausgabe der Tabelle). ❌ **Aber es bleibt viel zu langsam und schwankt: 33 s → 92 s → 3× >120 s.**
|
||
- 🔑 **Wo die Zeit hingeht (gemessen):** `images -q` 84 ms · `ps -aq` 95 ms · `volume ls -q` 65 ms · **`system df` 33–92+ s**. Also **vollständig in der Größenberechnung** (du-artiger Durchlauf der overlay2-/Volume-Verzeichnisse auf NAS-Platten) — ein struktureller Plattformnachteil, kein Defekt. Die Mengen sind harmlos (25 Images/13 GB, 10 Volumes/79 MB).
|
||
- **Aussichtsreichster Weg: eigener Portainer-Benutzer für HA ohne Zugriff auf ep14** → die Integration sieht die langsame Umgebung gar nicht. User-gated (Benutzer + Token anlegen). Alternativen: nichts tun (stört nichts) · Images aufräumen (6,98 GB reclaimable, Erfolg **offen**) · ep14 aus Portainer nehmen (schlechter Tausch).
|
||
- ⚠️ **Zwei widerlegte Zwischenthesen, nicht wieder aufgreifen:** die tote Umgebung `odroid-m1` war es **nicht** (entfernt, Symptom blieb, alle übrigen Endpoints ≤0,16 s); und es ist **kein** Größenproblem der Images.
|
||
- ✅ **Restliste aus dem TP5-Log-Lauf — ABGEARBEITET (Abnahme 2026-08-16: `out:bridge` = 0 über 10 min, Rate von ~166 auf ~34 Zeilen/min gefallen; im 43k-Zeilen-Log vom 08-17 kommt `out:bridge` nicht mehr vor).** Wert dieses Eintrags = die Diagnosemethode, nicht der Stand. Der Log-Lauf hatte gezeigt, dass **76 % allen Inter-VLAN-Verkehrs** aus VLAN20 nach VLAN1 (`out:bridge`) ging, obwohl dort **nichts mehr antwortete** (alle Ziele im ARP `failed`/`incomplete`). Verursacher waren Konfigurationen, die nie nachgezogen wurden:
|
||
- **`prometheus` (`10.40.20.151`, NAS)** scrapet 10 tote Ziele: `10.40.41.109:3000` · `.126:9443` · `.127:80` · `.147:5678` · `.149:3000` · `.152:3001` · `.153:9093` · `.154:9100` · `.159:6333` · `.23:80`.
|
||
- **`10.40.20.105`** zieht auf `10.40.41.100:8006` = **pve unter seiner alten VLAN1-Adresse** (real `10.40.20.100`).
|
||
- **`10.40.20.107`** auf `10.40.41.10:8080` (NAS alt).
|
||
- 📌 **Umzugsregel: das letzte Oktett bleibt gleich, nur `41`→`20`** — belegt für grafana (`.109`), vaultwarden (`.127`), Portainer (`.126`). Für `.23/.147/.149/.152/.153/.154/.159` existiert **kein** laufendes VLAN20-Gegenstück (anderswo oder tot; `alertmanager` ist seit TP14.1 als kaputt notiert) → diese Ziele gehören geprüft und dann aus Prometheus entfernt, nicht umgeschrieben.
|
||
- ⚠️ **Kein Firewall-/Portproblem** (2026-08-14 geprüft, nachdem beim Umstellen ein Timeout auftrat): `pveproxy` lauscht auf `*:8006`, `pve-firewall` ist *disabled*, keine iptables-Regel, kein `/etc/default/pveproxy`. `https://10.40.20.100:8006` antwortet aus VLAN10 **und** VLAN20 mit HTTP 200 in ~0,06 s. Wer hier einen Timeout sieht, sucht an der falschen Stelle — **und `timeout` gibt es auf macOS nicht**, ein `timeout bash -c '… /dev/tcp/…'`-Test meldet deshalb falsch-negativ.
|
||
- ⚠️ **Eskaliert 2026-07-15 (historisch — durch den WLAN-Rebuild 2026-08-08 gegenstandslos):** der unter TP3.1 dokumentierte VLAN1-Leak trat damals auch auf ap1/`dirtyvader-main` auf — die ap-seitige Interim-Härtung (TP3.1) milderte das Symptom, **die endgültige Lösung ist hier, TP19**: VLAN1 physisch weg = kein Leck mehr möglich.
|
||
- 🟢 **cudy-Stand (User 2026-07-16): cudy1 (`.41.246`) auf `10.40.10.246` umgehängt** — bewusst **kein Abbau-Ziel mehr**, dient als Standalone-WLAN-Sender während der Umstellungsphase (nicht Teil des Teardowns, extern vom User selbst umkonfiguriert). **cudy2 (`.41.247`) wird nicht mehr reaktiviert** — faktisch tot, ARP-Sweep bestätigt keine Präsenz mehr auf VLAN1. B2.2 („cudy1/cudy2 stromlos/Reset/ausbauen") ist damit **erledigt/gegenstandslos** (cudy2 aus, cudy1 schon migriert statt abgebaut).
|
||
- 🟢 **Live-Sweep 2026-07-16 (ap1, vor geplantem Teardown):** VLAN1 ist bereits viel weiter zurückgebaut als dokumentiert:
|
||
- `defconf`/pool-v1-DHCP-Server ist bereits **`disabled=yes`** (Export bestätigt) — B2.4 („defconf zurückziehen") ist **de facto schon erledigt**, Ursprung/Zeitpunkt dieser Änderung nicht aus dieser Session bekannt (nicht in dieser Doku protokolliert).
|
||
- ARP auf `bridge` (VLAN1): nur noch **2 „reachable"-Hosts** — `sw1` (`.41.11`, by design, bleibt bis MGMT-VLAN99) und **`pve`-Interim-Fallback (`.41.100`)** (B1.5-Aufräumziel, noch offen — Host-seitige Änderung auf pve, nicht Router). Alle anderen ~20 ARP-Einträge sind **stale/„failed"** (Cache-Reste längst migrierter Hosts wie macmini/.20.167, NAS/.20.10, grafana/.20.109 etc.), keine aktive Kommunikation mehr.
|
||
- **B1.1 MacBook (Claude-Sandbox-Client) bereits sicher migriert:** aktive DHCP-Leases nur noch auf `dhcp-v20` (`10.40.20.101`) und `dhcp-v10` (`10.40.10.187`), keine `defconf`-Lease mehr. ⚠️ Dual-Homed über 2 VLANs gleichzeitig (`project_dual_homing_slowness_incident`-Muster) — funktional ok, aber im Auge behalten.
|
||
- **B1.2 unidentifizierte Geräte `.41.133`/`.41.198`:** kein ARP-Eintrag mehr vorhanden — entweder migriert oder abgeklungen, gelten für den Teardown als **nicht mehr blockierend**.
|
||
- ⚠️ **B1.3 (Media→VLAN40) und B1.4 (saisonale Shellys) nicht in diesem Sweep verifiziert** — weiterhin offen, kurz vor dem finalen Cutover gegenchecken.
|
||
- **Backup-Stand vor Cutover (2026-07-16):** nur **ap1 gesichert** (Binary `bf-ap1-pre-vlan1-teardown-20260716.backup` auf dem Router + lokaler Export `routeros/state-backups/ap1-export-20260716-pre-vlan1-teardown.rsc`). **ap2 nicht gesichert, aber unkritisch:** bewusst ausgesteckt (User-bestätigt 2026-07-16, kein Ausfall) — kein Backup nötig, solange es aus bleibt. **sw1 nicht per Telnet gesichert** (`claudecode`-Login 2× deterministisch abgelehnt), aber **kein Blocker**: User macht sw1-Änderungen ohnehin direkt im UI/CLI selbst (effizienter als über den Claude-Runner, gemeinsame Entscheidung) — Telnet-Automation für sw1 ist hier kein Muss.
|
||
- **Ziel (Kapstein der Segmentierung, s. Zeile 27):** altes **cudy-WLAN weg** + **VLAN1 als Client-Netz stilllegen** (`defconf`/pool-v1-Client-DHCP zurückziehen, ✅ bereits disabled). **Nicht** VLAN1 komplett: Switch-/AP-Mgmt (sw1 `.11`/sw2 `.12`/ap2 `.2`) bleiben **by design** in VLAN1 (statisch, kein DHCP nötig) bis zum separaten **MGMT-VLAN99-Cutover** (eigener TP). ~~cudy1/cudy2 = `.41.246`/`.247` (Teardown-Ziele)~~ — cudy1 migriert auf `.10.246` (Reuse statt Abbau), cudy2 tot/nicht reaktiviert (s.o.).
|
||
- 🎯 **User-Gate (Update 2026-07-14): Shelly ist wieder da (kein Blocker mehr) — SwitchBot ist KEIN Prereq mehr, funktioniert laut User erst NACH VLAN1-Retirement (Reihenfolge ggü. 2026-07-13 umgedreht) → wandert in die Teardown-Nachlese.** TP19 damit direkt startbereit. Twinkly nebenher (saisonal).
|
||
- **Verlorene Shelly — IDENTIFIZIERT via HA 2026-07-13** (User-Hinweis „du siehst ihn im HA"): HA-Device `shellyplugsg3-e4b063e41ab8`, **Modell = Shelly Plug S Gen3**, **MAC `e4:b0:63:e4:1a:b8`**, HA-device_id `7a21e8f3349e6660f8b35974fc989a7b`. **NICHT im 26er-Inventar** (Spare/Küchen-Steckdose, die Christoph beim Einrichten verbockt hat; er nannte sie „küche s20", aber s20=`98:a3:16:7c:5f:ec` läuft sauber auf `.30.206` — Verwechslung). **KEIN AP-Mode, nicht tot:** assoziiert am **Altnetz `elektrosmog.c`** (cudy → untagged VLAN1-L2), aber mit **bogus-IP `10.95.10.206`** (Fremd-Subnetz, User-Fehler) → unrouting-bar, daher weder in VLAN1/30-Sweep noch normal per IP erreichbar, aber **per MAC in HA/WiFi-Table sichtbar**. Der `.219/s12`-Verdacht war Sackgasse (s12 nur saisonal aus).
|
||
- **Update 2026-07-14: Shelly wieder da** (User-Meldung) — Live-Status (noch bogus-IP `10.95.10.206` oder inzwischen normal erreichbar?) **zu Sessionbeginn per HA/ARP verifizieren**; Recovery-Plan unten ggf. obsolet, falls sich das Gerät selbst erholt hat.
|
||
- **Recovery-Plan (falls beim Verify noch nötig):** ap1 bekommt **temporäre Zweit-IP `10.95.10.1/24` auf der Bridge** → `10.95.10.206` per L2 erreichbar → **ein** `WiFi.SetConfig {sta: ssid=dirtyvader-iot, pass=IOT-PSK, static, ip=10.40.30.<frei>}` + `Shelly.Reboot` → Gerät kommt auf VLAN30 hoch → temp-IP entfernen. Danach `.30.x`-Reservierung + Inventar (MAC `e4:b0:63:e4:1a:b8`) + eco-Policy. Umgeht die „blutige Nase" (IP am Gerät ändern während man drauf hängt = Verbindungsverlust) — vom Router aus in einem Rutsch. Christophs manueller Direkt-Versuch (AP-IP auf 10.40.30 „verdrehen") war genau deshalb gescheitert.
|
||
- ✅ **SwitchBot floor-lamp** `.41.184` (`b8:f8:62:09:f2:04`) → **erfolgreich auf VLAN30 übersiedelt (2026-07-16, Christoph via App/BLE)**, Res. `.30.227`.
|
||
- **Prereqs-Status (live nachgezogen 2026-07-16, s. Live-Sweep oben):**
|
||
1. ✅ **Christophs Endgeräte → VLAN10/20 erledigt:** MacBook-Air (`c0:c7:db:de:71:be`, Claude-Sandbox-Client) hat aktive Leases nur noch auf `dhcp-v10`/`dhcp-v20`, keine `defconf`-Lease mehr. iPhone `.41.186` nicht im aktuellen Sweep erneut geprüft (zuletzt off/asleep) — kurz vor Cutover gegenchecken.
|
||
2. ✅ **2 unidentifizierte VLAN1-Geräte (`.41.133`/`.41.198`) aus dem ARP verschwunden** — kein aktueller Eintrag mehr, gelten als nicht mehr blockierend (entweder migriert oder abgeklungen).
|
||
3. ⏳ **Media → VLAN40 weiterhin offen:** Apple TV 4K + Philips TV nicht in diesem Sweep verifiziert.
|
||
4. ⏳ **Saisonale Shellys s1/s2/s3/s12 weiterhin offen** — nicht blockierend, kann übersprungen werden (Re-Onboarding-Aufwand akzeptiert).
|
||
5. ⏳ **pve-Interim-IP `.41.100` weiterhin aktiv/reachable** — Host-seitiger Cleanup auf pve selbst noch offen (nicht Router-Config).
|
||
- **Teardown-Sequenz (angepasst an den Live-Stand):** VLAN1-Client-Sweep ist **schon fast leer** (nur sw1-Mgmt + pve-Interim reachable) → `defconf`/pool-v1 ist **bereits disabled** → cudy-Abbau **entfällt** (cudy1 umgenutzt statt abgebaut, cudy2 tot) → ✅ **SwitchBot + Twinkly erledigt (2026-07-16)** → **verbleibt:** Media-Check (B1.3), pve-`.41.100` abräumen (B1.5), Interim-Krücken bewerten (ap2 `frame-types=admit-only-vlan-tagged`, `st-main rrm/wnm`), Inventar/`ip-plan`/DNS `.41→Ziel` final.
|
||
- Abh.: **TP14 ✅** (Hosts raus aus VLAN1), **TP17 ✅** (Shellys raus). Verwandt: `project_vlan1_dhcp_incident` (cudy-Dual-DHCP-Leak), TP5 (Deny-by-default danach sauberer). SSoT hier + `docs/tp19-vlan1-cudy-teardown-runbook.md`. Folge: **TP19.1**.
|
||
|
||
#### TP19.1 — VLAN99-MGMT-Cutover (sw1/sw2/ap2-Mgmt VLAN1 → VLAN99) ▸ ✅ ERLEDIGT (2026-08-14 nachgezogen) — alle lebenden Geräte auf VLAN99, die beiden Rest-Punkte gegenstandslos
|
||
- ✅ **Abschluss-Nachtrag 2026-08-14 (Status-Korrektur, kein neuer Change):** der Header stand noch auf „ap2 vertagt, sw1 offen" (Stand 2026-07-16) — beides ist durch spätere TPs eingelöst bzw. gegenstandslos geworden, ohne dass es hier nachgetragen wurde. Ist-Stand: **core `.99.1` · branch `.99.2` · ap1 `.99.11` · ap2 `.99.12`** — jedes lebende Netzgerät hat sein Mgmt auf VLAN99. **sw1/sw2 existieren nicht mehr** (physisch ausgebaut, in Nautobot per TP22 Schritt 5 auf *Decommissioned*). Damit ist TP19.1 vollständig; offen bleibt nur der VLAN1-**Client**-Teil, und der gehört zu TP19 (Hauptteil), nicht hierher.
|
||
- **Scope:** die einzigen Dinge, die TP19s Hauptteil bewusst in VLAN1 belässt — sw1 (`.41.11`), sw2, ap2-Mgmt (`.41.2`) — auf VLAN 99 (MGMT, `10.40.99.0/24`) umziehen. Erst danach ist VLAN1 wirklich **komplett** weg, nicht nur als Client-Netz.
|
||
- **User-Entscheid (2026-07-16):** kein eigenständiges TP-Nummer — bewusst als **Unterpunkt von TP19** geführt („VLAN1 killen"), nicht als TP22 o.ä.
|
||
- ✅ **ap1-Teil erledigt (2026-07-16):** ap1 brauchte keinen funktionalen Umzug (vlan99-Interface/GW `.99.1` läuft schon seit TP4) — nur der kosmetische DNS-Static-Nachtrag `ap1.bf.lan` `10.40.41.1`→`10.40.99.1` war offen, per MikroMCP erledigt (dry-run→apply, `manage_dns_entry`).
|
||
- ✅ **sw2-Teil erledigt (2026-07-16, Christoph selbst über die Klickanleitung):** sw2 jetzt auf **`10.40.99.12`** (statisch, planmäßiger Zielwert aus `ip-plan.md`) — per Ping verifiziert. Zwischenzeitlich war das Gerät kurz auf einer *dynamischen* `10.40.99.200`-Lease aufgetaucht (unklare Ursache, s. Memory `project_tp20_tp21_fasttrack_20260716`), jetzt aber sauber auf der geplanten statischen Adresse. `CLAUDE.md`/`ip-plan.md` nachgezogen.
|
||
- ✅ **ap2-Teil erledigt (2026-08-08, im TP3-Rebuild) — die Vertagung wurde wie geplant eingelöst.** ap2-Mgmt liegt auf **`10.40.99.12`** (VLAN99), gesetzt bei der Standalone-Neuprovisionierung, nicht isoliert von Hand; `primary_ip4` in Nautobot per TP22 Schritt 5 nachgezogen. Der `ap2.bf.lan`-DNS-Static wurde im TP22-Pilot-Deploy (2026-08-13, GC-ConfigPlan) von `10.40.41.2` auf `.99.12` gezogen. Ursprüngliche Vertagungs-Begründung zur Historie:
|
||
- 🔜 **~~ap2-Teil bewusst vertagt~~ (User-Entscheid 2026-07-16): erst NACH Nautobot (TP21).** Nautobot übernimmt dann in einem Rutsch ap1 + ap2 + die neuen CRS326-Switches (TP15) als config-as-code-Fleet — der ap2-Mgmt-VLAN-Umzug wird also nicht mehr isoliert von Hand gemacht, sondern Teil der Nautobot-Erstadoption. `ap2.bf.lan`-DNS-Static bleibt bewusst auf `10.40.41.2` bis dahin.
|
||
- 🔒 **TP19.1 war die letzte Ausnahme vom ap1/ap2-Config-Freeze — der jetzt (2026-07-16) zur generellen Regel ausgeweitet wurde** (s. Governance-Entscheid oben): **jede** Gerätekonfiguration künftig ausschließlich über Nautobot, keine direkten Änderungen mehr an einzelnen im Netz verstreuten Geräten. Der ap1-DNS-Fix oben war die letzte direkte MikroMCP-Write-Aktion unter der alten (engeren) Regel.
|
||
- ⚪ **sw1-Teil GEGENSTANDSLOS (2026-08-14 festgestellt):** Christoph hat sich für „auf CRS326 warten" entschieden — sw1 wurde am **2026-08-08 durch `crs326-core` ersetzt und physisch ausgebaut** (Nautobot: *Decommissioned*, TP22 Schritt 5). Ein Mgmt-VLAN-Umzug für ein nicht mehr existierendes Gerät entfällt ersatzlos. **`switch/ui-guides/02-vlan99-mgmt-cutover.md` ist damit historisch** (nie ausgeführt, nicht mehr ausführbar — der Telnet-Runner `switch/` insgesamt nur noch Archiv). Ursprünglicher Stand zur Historie: Klickanleitung fertig, Ausführung on-demand (User-Entscheid 2026-07-16), nicht blockierend.
|
||
- **Bereits vorbereitet:** `docs/ip-plan.md` Phase 2 hat schon einen Entwurf (ap2→`10.40.99.2`, sw1→`10.40.99.11`, „Lockout-Risiko, sorgsam", sw1 zuletzt).
|
||
- **Abh.:** TP19-Hauptteil (VLAN1-Client-Retirement) — logisch danach, da VLAN99 als eigenes Mgmt-Netz erst dann wirklich nötig wird, wenn VLAN1 sonst leer ist. sw1-Teil braucht sw1-Telnet-Zugriff (User macht sw1-Changes direkt, s. Feedback-Memory) — Lockout-Risiko am größten bei sw1 (einziger Netzpfad zu sw2 hängt am Uplink).
|
||
|
||
<a id="tp20"></a>
|
||
|
||
### TP20 — Platform-Datenbank (Postgres Precious-Tier + MariaDB Tier-2) ▸ ✅ **Plattform live (2026-07-16)** — Nautobot-DB angelegt, vw-Migration (TP20.1) offen — Memory `project_mikrotik_commit_nautobot_gc`
|
||
- ✅ **Platform-Postgres live (2026-07-16):** LXC 105 auf pve, `platform-pg.bf.lan` / `10.40.20.20`, Ubuntu 24.04 (native PG16-Paket, kein PGDG-Repo nötig — bewusste Abweichung von der sonstigen Debian/community-scripts-Konvention der übrigen pve-LXCs, aus PG16-Verfügbarkeitsgründen). Cluster mit `--locale en_US.UTF-8 -e UTF8` neu initialisiert (Erstinstallation lief in `C`/`SQL_ASCII`, für Nautobot ungeeignet, korrigiert). `wal_level=replica` gesetzt; **`archive_mode` bewusst noch NICHT an** — ohne pgBackRest/Garage-S3 (TP18) zum Prunen würde archivierte WAL unbegrenzt wachsen und die kleine 8GB-Disk füllen; wird nachgezogen sobald TP18 steht. Rolle+DB `nautobot` angelegt, `pg_hba.conf` erlaubt `scram-sha-256` aus `10.40.20.0/24`. End-to-End-Verbindungstest (Netzwerk, nicht nur localhost) erfolgreich. Creds in `postgres/.secrets` (gitignored, `chmod 600`).
|
||
- **Auslöser:** Mehrere Dienste brauchen zentrale DB (Nautobot-SoT [TP21], Vaultwarden, HA-Recorder). Grundsatzentscheid statt Nautobot-Insellösung — „wir skalieren schneller als wir schauen können" (User).
|
||
- ⚠️ **Abh.-Korrektur (User 2026-07-16): TP18 (Garage-S3) ist KEINE Vorbedingung mehr — „hat mit dem Netz nichts zu tun", transitiv aus der TP21-Kette entfernt.** pgBackRest→Garage-S3 (s.u.) wird **nachgezogen sobald Garage existiert**, blockiert aber nicht mehr den Start. `wal_level=replica`/`archive_mode` können mit einem Platzhalter/lokalen Archive-Ziel hochgezogen werden, produktives Off-Site-PITR kommt später.
|
||
- ⚠️ **Komodo-Abh. (s. TP2/Memory `project_komodo_tp_pending`) vorerst zurückgestellt:** Komodo selbst existiert noch nicht als laufendes System — kann also nicht „darüber" deployt werden. **Entscheidung (Claude, 2026-07-16, transparent statt stillschweigend): Postgres-LXC + Nautobot-Stack werden jetzt direkt gebaut (LXC/Docker-Compose), Komodo adoptiert sie später** — gleiches Muster wie bei den CRS326 (Hardware/Stack zuerst lauffähig, Management-Layer zieht nach). Sag Bescheid, falls das nicht gewünscht ist.
|
||
- 🔍 **Ressourcen-Check 2026-07-16 (live geprüft, SSH):**
|
||
- **pve** (10.40.20.100): 4 vCPU, **15 GB RAM total, ~12 GB belegt, nur ~2-3 GB frei, 3 GB bereits im Swap** — eng! Größter Verbraucher: HAOS-VM (`haos`, vmid 100) mit ~8,2 GB aktiv von 10 GB Limit. Disk unkritisch (94 GB Root, 85 GB frei).
|
||
- **Mac mini** (10.40.20.167): 10 CPU, 24 GB RAM, **nur ~1,9 GB "wired" (nicht reklamierbar)** — deutlich mehr echter Headroom als pve trotz "23G used"-Anzeige (macOS-Cache-Artefakt). Docker läuft bereits dort (n8n, n8n-redis, cloudflared×2, gitea-mcp, portainer_agent).
|
||
- NAS nicht geprüft (kein funktionierender SSH-Zugriff von hier aus mit den vorhandenen Keys/User) — für diese beiden Stacks laut TP20/TP21-Plan ohnehin nicht Zielhost.
|
||
- **✅ Entschieden (2026-07-16): Postgres-LXC auf pve, Nautobot-App-Stack auf NAS** (nicht Mac mini) — NAS hat mehr Headroom (~12 GB verfügbar) und hostet bereits **Gitea** (Nautobots Git-Backend für Golden-Config-Templates), kürzere Wege als Mac mini. Deploy-Weg: **Portainer-Stack auf ep14**, analog dem bestehenden Muster (`docs/nas-vlan20/stacks/*.yml`), Basis = offizielles `nautobot-docker-compose` (github.com/nautobot/nautobot-docker-compose), nicht von Null gebaut. Wird als **separate Session** angestoßen (parallel zum Postgres-LXC-Aufbau auf pve).
|
||
- **Nachtrag (2026-07-16, nach Aufräumen): pve hat sich entspannt** — nach Abschalten mehrerer mittelfristig eh stillzulegender Services (User) jetzt **6,7 GB frei, 0 Swap** (vorher 2-3 GB frei, 3 GB Swap). Postgres-LXC passt entspannt.
|
||
- **RAM-Upgrade-Idee (32GB-Stick) zurückgestellt (User 2026-07-16):** Intel N100 (pve-CPU) ist offiziell auf max. 16 GB spezifiziert + Single-Channel; ein dokumentierter Fall mit demselben Corsair-Modul auf vergleichbarer N100-Hardware endete in Memtest86-Fehlern, stabil erst nach Rückbau auf 8GB. Empfehlung: 16GB als Decke für diese Kiste behandeln statt auf einen Settings-Fix zu hoffen. **User rechnet ohnehin mittelfristig mit einem HW-Tausch für pve** — gehört ggf. in TP15s HW-Refresh-Scope, noch nicht formal aufgenommen.
|
||
- **✅ 2-Tier (User 2026-07-14):**
|
||
- **Tier-1 „Platform-Postgres" (precious):** EIN Postgres-Cluster, getrennte DBs für **Nautobot** (hier provisioniert) **+ Vaultwarden** (Onboarding = **TP20.1**). **PG 16** (Nautobot-Matrix-safe; **Nautobot unterstützt kein MariaDB**, nur PG/MySQL-8 → Postgres ist gesetzt, nicht optional).
|
||
- **Tier-2 (Bestand):** **HA-Recorder bleibt MariaDB** (Write-Churn, andere Zuverlässigkeitsklasse, single-site). *(Vaultwarden liegt heute ebenfalls auf MariaDB → wandert per TP20.1 nach Tier-1.)* Engine-Split fällt sauber entlang der Tier-Grenze.
|
||
- **✅ Single-Site-first (User 2026-07-14):** jetzt nur BF-Primary („gehostet wird vorerst auf der bf-site", 2026-07-16 reconfirmed). Nimmt das **2-Site-Quorum/Split-Brain**-Problem (bräuchte 3. Witness) vom Tisch. **ABER replication-ready ab Tag 1:** `wal_level=replica`, `archive_mode=on` (Ziel vorerst lokal/Platzhalter, s.o.) — späterer Standby = `pg_basebackup`, **kein** Primary-Umbau/Restart. pgBackRest→Garage-S3 folgt nach TP18, nicht mehr Blocker.
|
||
- **Substrat:** Postgres in **LXC auf pve** (DB-Klasse, nicht Docker).
|
||
- **Scope TP20 = Plattform:** Cluster hoch, Tiering, Backup/PITR, **Nautobot-DB** anlegen. **vw-Migration + Cross-Site-HA = TP20.1** (ausgelagert).
|
||
- **Offene Sub-Steps:** PG-Version final (16 vs 17); Rollen/DB-Anlage Nautobot; pgBackRest-Bucket in Garage folgt **nach** TP18 (nicht mehr Blocker, s.o.).
|
||
- ⚠️ **Komodo-Abh. (User 2026-07-16) vorerst zurückgestellt, s.o.:** ursprünglich als Blocker gedacht (TP20 + neue Softwarepakete über Komodo statt direkt/Portainer), aber Komodo existiert noch nicht lauffähig und hat keine eigene TP-Nummer (nur unter TP2/TP11 erwähnt) → **entkoppelt, um die Montag-Deadline zu halten.** Komodo-TP-Nummer weiterhin zu klären, aber nicht mehr auf dem kritischen Pfad für TP20-Start.
|
||
- Abh.: **keine harten Blocker mehr** (TP18 entfernt, Komodo entkoppelt). **Blockt weiterhin TP21 + TP20.1.**
|
||
|
||
<a id="tp21"></a>
|
||
|
||
### TP21 — Nautobot Golden Config (config-as-code Control-Plane) ▸ 🟢 **Plattform + Datenmodell + Templates live**
|
||
- 🔴 **BETRIEBSBEFUND 2026-08-18: der Stack hat sich zweimal in einer Nacht selbst umgebracht — Ursache
|
||
war der Image-Healthcheck, nicht Celery.** `nautobot-worker` und `-scheduler` erben aus dem Image
|
||
einen `HEALTHCHECK`, der alle **10 s** `nautobot-server health_check` startet — einen kompletten
|
||
Django-Bootstrap (~250 MB). Unter Last dauert ein Lauf **>2 min**, die Aufrufe stapeln sich also
|
||
unbegrenzt (**neun gleichzeitig gemessen**, aeltester 2:16 alt). Ergebnis: Speicher alle →
|
||
`proc_fork.c:189 Failed to fork(). errno=[12/Cannot allocate memory]` im NAS-Syslog um 23:48:47 →
|
||
eine Minute spaeter raeumt DSM den Stack ab (sauberes `docker stop`, daher ExitCode 0 bzw. 137
|
||
nach Grace — **kein** Kernel-OOM, `OOMKilled=false`). Dasselbe Muster schon um 19:35.
|
||
- **Behoben im Stack** (`docs/nas-vlan20/stacks/210-nautobot.yml`, Portainer-Stack 203):
|
||
`healthcheck: disable: true` auf worker+scheduler, `--concurrency=2 --max-tasks-per-child=100`,
|
||
`mem_limit` je Container, Web-Healthcheck-Timeout 5s → 15s. **Load fiel von 73,5 auf 5,8.**
|
||
- ⚠️ **`cpus:` geht auf Synology NICHT** — der Kernel hat keine CFS-Quota, der Stack-Deploy bricht
|
||
mit `NanoCPUs can not be set` ab. Nur `mem_limit` verwenden.
|
||
- 📌 **Nichts haengt an dieser Health:** `restart=unless-stopped` startet bei *Exit* neu, nicht bei
|
||
*unhealthy*, und kein `depends_on` wartet auf den Worker. Der Web-Container behaelt seinen
|
||
billigen curl-Check — das ist das Signal, das `service_healthy` nutzt.
|
||
- 🎯 **Strukturell bleibt es eng:** Nautobot teilt sich die DS923+ (4 Kerne) mit einer VMM-VM,
|
||
Active Backup und ~14 weiteren Containern. Der Stack bleibt vorerst dort (User-Entscheid
|
||
2026-08-18); **ein groesserer Server ist gedanklich bereits vorgemerkt**.
|
||
- 🔴 **Die Kehrseite des Re-Baselines, am eigenen Leib gelernt (2026-08-18):** `ingest-core-firewall.py`
|
||
erklärt den **Ist-Stand zum Soll** — es prüft nicht, ob der Ist-Stand sinnvoll ist. Beim Lauf nach
|
||
TP5 E3/E4 ist damit die **kaputte Fasttrack-Reihenfolge** des Core als Absicht in den Config-Context
|
||
gewandert (s. TP6). Der Warnhinweis im Skriptkopf („nur nach einer bewusst durchgeführten,
|
||
verifizierten Änderung laufen lassen") ist also **notwendig, aber nicht hinreichend**: die Änderung
|
||
war bewusst, die *Umgebung*, die mitgeschrieben wurde, hat aber niemand angesehen.
|
||
**Merkregel: nach dem ersten Re-Baseline einer Sektion deren Diff einmal ganz lesen**, nicht nur die
|
||
Zeilen, die man selbst angefasst hat.
|
||
- ✅ **Nachtrag 2026-08-19: der konkrete Fall ist aufgelöst** — Christoph hat entschieden, die
|
||
Fasttrack-Reihenfolge **so zu lassen** (Begründung in TP6). Der Eintrag in der SoT ist damit
|
||
nachträglich korrekt. **Die Merkregel bleibt trotzdem gültig:** dass ein versehentlich
|
||
mitgeschriebener Zustand sich hinterher als brauchbar herausstellt, ist Glück, kein Verfahren.
|
||
- 🔧 **Zwei echte Template-Fehler, beim Re-Baseline 2026-08-18 gefunden** (beide gepusht):
|
||
- `/ip service set … **address=**` → RouterOS 7.23/7.24 exportiert **`available-from=`**. Das
|
||
erzeugte vier Dauer-Drifts (ap1/ap2/branch/core), bei denen das **Geraet** recht hatte und das
|
||
Soll falsch war — und ein Push haette auf 7.24 auflaufen koennen.
|
||
- `hw-offload` gehoert in die SKIP-Liste von `ingest-core-firewall.py`: die REST-API meldet es auf
|
||
der defconf-fasttrack-Regel, `/export` schreibt es nicht → ewige Falsch-Drift auf genau einer Regel.
|
||
- Ergebnis: **71/76 compliant**; die verbleibenden 5 sind der TP28-Rest (`wifi-*` auf dem Core). (App 2026-07-22, Basisdaten + Git-Backend + L3-Core-Template 2026-08-05, ap1/ap2 As-built 2026-08-08); **Device-Adoption/Compliance-Push ausgelagert → TP22** — Memory `project_mikrotik_commit_nautobot_gc`
|
||
- **Ziel:** modellierter Control-Plane über den MikroTik-**RouterOS**-Kern: GUI-SoT + **Config-Compliance/Drift** + intended-config + Push. **Ergänzt** MikroMCP (interaktiv/Debug), **ersetzt es nicht** (deklarativ/DR/Compliance). Aus User-Recherche 2026-07-14: MikroTik in Golden Config **erstklassig** (`mikrotik_routeros` + `_api`, Nornir/NAPALM, backup+deploy).
|
||
- **Scope-Grenze (Fassung 2026-08-16, nachgezogen):** RouterOS in-model = **`crs326-core` (L3-Router) + `crs326-branch` (L2) + ap1/ap2 (reine APs)** — alle 4 Geräte sind seit TP22 adoptiert, Golden Config deckt damit den **ganzen Fabric inkl. WLAN**. Es bleibt **nichts** außerhalb: der frühere Vorbehalt „außerhalb bleibt Omada-WLAN" ist gegenstandslos, seit der Omada-Stack gelöscht (2026-07-16) und CAPsMAN abgeschafft (2026-08-08) ist.
|
||
> *Vorherige Fassung (überholt): „RouterOS in-model = Router (ap1/ap2, dauerhaft — kein RB5009) + beide CRS326-Switches (RouterOS-Boot) → Golden Config deckt den ganzen Kabel-Fabric. Außerhalb bleibt nur Omada-WLAN (eigener Controller, spielt aber laut TP15-Korrektur ohnehin keine Rolle mehr)."*
|
||
- 🔒 **Wird zum alleinigen Config-Weg für Netzgeräte (Governance-Entscheid 2026-07-16, s. oben):** ab hier keine ad-hoc MikroMCP-Writes/Switch-UI-Klicks mehr auf Dauer — Nautobot managed den Fleet.
|
||
- ✅ **Nautobot-Stack live (2026-07-16), auf NAS statt Mac mini** (Ressourcen-Check hatte Mac mini vorgeschlagen, dann auf NAS umentschieden — mehr Headroom + Gitea-Co-Location, s. TP20-Log): Portainer-Stack `nautobot` (Id 203, ep14), 4 Container — `nautobot` (App/API, `10.40.20.30:8080`), `nautobot-worker` (Celery), `nautobot-scheduler` (Celery Beat), `nautobot-redis`. Bild `ghcr.io/nautobot/nautobot:3.1.7` (offizielles Image direkt, **nicht** das `nautobot-docker-compose`-Repo — das ist ein `invoke`/Poetry-getriebenes Dev-Tooling-Repo ohne eigenständige `docker-compose.yml`, für einen Portainer-String-Stack ungeeignet; bewusste Abweichung, dokumentiert statt stillschweigend). DB = Platform-Postgres (`10.40.20.20`, TP20). Compose-Datei `docs/nas-vlan20/stacks/210-nautobot.yml` (gitignored, wie alle NAS-Stack-Files).
|
||
- 🐛 **Gotcha beim ersten Deploy:** alle 3 Nautobot-Container liefen initial gleichzeitig `migrate` (Standard-Entrypoint-Verhalten) → Race-Condition, `IntegrityError` auf `pg_type`. Fix: `nautobot`-Service kriegt einen `healthcheck` (`curl .../health/`), `nautobot-worker`/`nautobot-scheduler` starten erst bei `depends_on: nautobot: condition: service_healthy` — serialisiert die Migration sauber auf einen Container. DB dafür einmal `DROP`+`CREATE` (sauberer Neustart, kein Datenverlust — war eh leer).
|
||
- ✅ **End-to-End verifiziert:** `/api/status/` mit Token 200, Nautobot-Version 3.1.7, alle 4 Container laufen. Superuser `admin` angelegt, Creds im Stack-File (gitignored) + `postgres/.secrets` (DB-Seite).
|
||
- ⚠️ **Bekannt offen, nicht blockierend:** kein HTTPS/Reverse-Proxy vor Nautobot (Django-Security-Warnings beim Start, erwartet für internen HTTP-only-Betrieb auf VLAN20 — analog anderen internen Diensten); Celery-Worker-Registrierung beim allerersten Start nicht sofort sichtbar (`celery-workers-running:0` initial), unkritisch, braucht kurz zum Einpendeln.
|
||
- **PoC-Pfad (angepasst an die Montag-Deadline):** ~~Nautobot-Stack hoch~~ ✅ → **ap1 + ap2 in einem Rutsch adoptieren** (ap2 bekommt dabei gleich seinen VLAN99-Mgmt-Umzug aus TP19.1, s.o. — nicht isoliert vorher) → sobald CRS326 verkabelt sind, **direkt als Nautobot-verwaltete Geräte adoptieren statt erst manuell konfigurieren** → Compliance/Drift + Templates/intended für VLAN-/Firewall-Standards danach.
|
||
- ✅ **Basisdaten-Ingest durchgeführt (2026-07-22)** — Ist-Zustand, nicht Zielzustand (User-Entscheid: „wir gehen vom status quo aus"). Quelle = ap1 live via MikroMCP. Skript-Muster idempotent (get-or-create + Delta-PATCH).
|
||
- **Locations:** neuer LocationType `Bereich` unter `Site`; `BF → Technik UG` (crs326-core, crs326-branch, ap2) und `BF → Wohnzimmer Sideboard` (ap1).
|
||
- **Devices:** ap1 = Rolle *Router* (Ist **am 2026-07-22** — ⚠️ seit der Adoption 2026-08-13 ist ap1 in Nautobot *Wireless AP* und `crs326-core` der *Router*, s. TP22), ap2 = neue Rolle *Wireless AP* + Status **Offline**, beide CRS326 **Platform RouterOS nachgetragen** (fehlte → hätte jeden GC-Job auf ihnen verhindert) + Werks-MAC/software-id in `comments`. Neu als Übergangs-Devices **ohne Platform** (nie GC-fähig, bewusst): `sw1` (ALLNET), `sw2` (ES208G), `cudy1`.
|
||
- **IPAM:** VLAN-Group `BF` + **9 VLANs** (1 *Deprecated*, 10/20/30/40/50/90/99/100 aktiv) + **8 Prefixes**, jeweils an ihr VLAN gebunden. VLAN100/WAN bewusst ohne Prefix (öffentliches Liwest-/24, gehört uns nicht).
|
||
- **ap1-Interfaces (26):** ether1-5, bridge, lo, wg-hub, vlan10-100, wifi1/wifi2, 7 VAPs — inkl. MACs, `ether2` als Trunk (untagged VLAN1 + tagged 10-100), VAPs als Access-Ports auf ihrem VLAN. 9 Gateway-IPs an ihren Interfaces. Platzhalter-Interface `mgmt` auf ap1 gelöscht (existiert real nicht).
|
||
- **WLAN:** logischer Controller `bf-wlan` (**kein** CAPsMAN — `controller_device` bewusst leer, die Control-Plane *ist* Nautobot GC) + Gruppe `BF-WLAN-APs` (ap1+ap2) + **6 WirelessNetworks mit VLAN-Bindung** + 2 RadioProfiles (2,4 GHz ch1 / 5 GHz ch36, Domain AT).
|
||
- 🐞 **API-Gotcha:** `tagged_vlans` wird vom REST-Serializer in Detail-/List-Views als `null` zurückgegeben, **obwohl korrekt gespeichert** — Verifikation nur über **GraphQL** verlässlich. Kostet sonst eine Stunde Fehlersuche an einem Nicht-Problem. Ebenso: Relationen kommen per Default ohne `display` (`?depth=1` nötig), und `VLANGroup`/`Prefix`/`VLAN` brauchen passende `content_types` am LocationType, sonst 400.
|
||
- ✅ **CRS326-Core L3-Device-Modell + Ports angelegt (2026-08-05):** `crs326-core` jetzt Rolle *Router*, alle VLAN-SVIs (`vlan10/20/30/40/50/90/99/100`) + Gateway-IPs `.1` (außer vlan100) in Nautobot; Portbelegung via `nautobot/ingest-crs326-ports.py` aktualisiert. `crs326-branch` bleibt *Switch* mit Mgmt-IP `.99.13` (Zielrenumber `.12` im Cutover-Fenster).
|
||
- ⚠️ **Offen/nachzutragen:** exaktes Modell von sw1 + cudy1 (aktuell `… (Modell TBD)`); Standort von sw1/sw2/cudy1 (hängen an `BF` statt an einem Bereich); ~~ap2s `primary_ip4` zeigt noch auf das tote `10.40.41.2`~~ → ✅ **erledigt (TP22 Schritt 5): `10.40.99.12`.**
|
||
- ✅ **Git-Backend an Gitea angebunden (2026-08-05):** Repository `bf-golden-config` (`jinja_templates/mikrotik_routeros.j2`) sync-t in Nautobot; Generate Intended Config für `crs326-core` erfolgreich, intended config in GC-Tabelle vorhanden.
|
||
- ✅ **Lokale Cutover-Config materialisiert:** `routeros/tp15-core-cutover-intended.rsc` enthält die aus Nautobot gerenderte Config plus die 54 ap1-Leases und den WG-Hub-Privkey (lokales Fenster-Artefakt, **nicht** im Golden-Config-Repo).
|
||
- **↪️ Adoption + Compliance-Push → ausgelagert nach TP22** (eigener TP, User-Entscheid 2026-08-08): ap1/ap2/crs326-Adoption, RouterOS-Nornir-Dispatcher, `enable_deploy`, WG-Privkey/PSK-Secret-Handling. TP21 selbst = Plattform + Datenmodell + Templates (steht). Datenmodell-Nachträge (branch/core-Ports final, sw1 außer Betrieb, cudy1) laufen in TP22 mit.
|
||
- Abh.: **TP20** (Postgres muss stehen), Gitea ✅, RouterOS-Fleet (core/branch/ap1/ap2).
|
||
|
||
<a id="tp21-1"></a>
|
||
|
||
### TP21.1 — Core-Cutover-Recovery (ap1 → crs326-core als L3-Router) ▸ ✅ NETZ WIEDER LIVE (2026-08-08); ~~Aufräum-Todos offen~~ → **alle fünf erledigt, letzter live gegengeprüft 2026-08-23** (s. Nachtrag am Ende)
|
||
> **Kern-Erkenntnis (wichtig gegen Doku-Verwirrung):** **`crs326-core` IST jetzt der produktive L3-Router.** Der Core-Cutover (TP15) wurde faktisch vollzogen — ap1 wurde plattgemacht/getrennt, core hat Routing/DHCP/DNS/NAT/WG-Hub übernommen. core-Mgmt = **`10.40.99.1`** (vlan99-SVI) bzw. same-subnet-Fallback **`10.40.10.1`** (RouterOS-Version 7.19.5 identifiziert das Gerät zweifelsfrei als CRS326, nicht als hAP/ap1). Die CLAUDE.md-Zeile „core grundkonfiguriert, NICHT produktiv" ist damit **überholt**.
|
||
- **Verlauf:** Ausgangssymptom war ein L3-Ausfall-Eindruck (kein DHCP, „kein Routing nach .20.x"). Ursache = **unvollständiger Cutover**: core hatte L3-SVIs + Firewall, aber es fehlten DHCP-Server, WAN-Egress, WG-Hub und der DNS-Resolver-Upstream. ap1 (bisher alleiniges L3-Gerät) war bereits Werksreset + getrennt.
|
||
- ✅ **DHCP wiederhergestellt** aus letztem ap1-Backup (`routeros/ap1-pre-core-cutover-20260806-073616.rsc`): 7 Server (`dhcp-v10…v99`) + 8 Networks + **54 statische Reservierungen** + Option `dns 1.1.1.1`. Reproduzierbar in `routeros/tp21-core-dhcp-from-ap1.rsc`. Legacy VLAN1 (pool-v1/defconf) + tote omada-inform-Option bewusst weggelassen.
|
||
- ✅ **WAN/Internet:** dhcp-client auf `vlan100` → Liwest-Public-IP `90.146.33.177` gebunden (Arris hängt physisch an core.vlan100), NAT-Masquerade `out-interface-list=WAN`, Default-Route korrekt; die self-pointing Blackhole-Route `0.0.0.0/0 via 10.40.99.1` entfernt. **disabled-Falle beachtet:** die ap1-Masquerade-Regeln `out-interface=bridge`/`=ether1` sind `disabled=yes` und wurden NICHT übernommen.
|
||
- ✅ **WG-Hub (TP10)** neu aufgebaut: `wg-hub` (listen 51821, Privkey aus `routeros/ap1-wg-hub.privkey`, Hub-Pubkey unverändert) + Peers `fm`/`dw` + Routen. **Beide Tunnel handshaken** (dw + fm live). Reproduzierbar (Privkey redacted) in `routeros/tp21-core-wan-wg-restore.rsc`.
|
||
- ✅ **DNS gefixt:** core hatte `allow-remote-requests=no` + leeren Upstream → Client-DNS tot UND Hub konnte myfritz (FM-Endpoint) nicht auflösen. Fix = `allow-remote-requests=yes` + `servers=1.1.1.1` (**plain**, weil core `certs=0` → `verify-doh-cert` unmöglich). Das entsperrte auch FM.
|
||
- ✅ **Identity** auf `crs326-core` gesetzt (war fälschlich „ap1" — genau diese Altlast stiftete Verwirrung). `mcp/mikromcp/config/routers.yaml` auf Zielschema: core `.99.1`, branch `.99.2`, ap1 `.99.11`, ap2 `.99.12` (wirkt nach MCP-Neustart; danach core via routerId `crs326-core`).
|
||
- ✅ **disabled/invalid-Falle bereinigt (User-Hinweis „keine disabled-Falle?"):** (a) die 2 TP10-WG-Forward-Regeln (`allow from/to WG spokes`) waren `I`=invalid, weil aus dem ap1-Backup mit der **stale Interface-ID `*1D`** übernommen → auf `in/out-interface=wg-hub` (per Name) korrigiert; (b) 2 **inaktive WG-Route-Leichen** (`gateway=""`, aus einem früheren Cutover-Versuch vor Existenz von wg-hub) entfernt, nur die aktiven `gateway=wg-hub` bleiben. **Finaler Sweep sauber:** filter-invalid=0, nat-invalid=0, routes-inactive=0, if-disabled=2 (nur ungenutzte `sfp-sfpplus1/2`, gewollt).
|
||
- ✅ **Backups:** `core-pre-dhcp-restore-20260808.backup` (vor Eingriff) + `core-post-tp21-recovery-20260808.backup` (nach allen Fixes neu gezogen) auf dem Gerät.
|
||
- ℹ️ **branch** ist gesund (L2, forwardet), Mgmt `10.40.99.2` (~~+ temp `10.40.10.2`~~ — die Temp-IP ist am 2026-08-16 entfernt worden) — via core-MNDP bestätigt. Das in Winbox-MAC gesehene „0.0.0.0" ist der bekannte Discovery-Artefakt (Gerät hat auf dem Discovery-VLAN keine IP), kein echtes Problem.
|
||
- 🔜 **TODO (Aufräum-Liste, spätere Session — danach WLAN-Neuprovisionierung):**
|
||
1. **DoH-Cloudflare-Gateway auf core wiederherstellen** (aktuell nur plain 1.1.1.1, keine Filterung/Account-Bindung): CA-Bundle importieren → `use-doh-server=https://px35ynlol3.cloudflare-gateway.com/dns-query verify-doh-cert=yes` (Referenz-Config liegt auf **ap1**).
|
||
2. **Intended config nach Nautobot zurückschreiben** (die Direkt-Writes liefen unter der CRS326-Cutover-Governance-Ausnahme).
|
||
3. **branch `192.168.88.1`-Rest verifizieren** (Bootstrap Rev 4 entfernt es in Zeile 70; via MCP noch nicht gegengeprüft, weil Registry-Umstellung MCP-Neustart braucht).
|
||
4. ✅ **core `ether4` GEKLÄRT (2026-08-13) — es ist das Wohnzimmer-Sideboard (Smart-Managed-Switch, nur VLAN40-Multimedia), und der Port ist der falsche.** Symptom war „TV meldet, LAN funktioniert nicht".
|
||
- **Ursache = VLAN-Mismatch, kein Kabeldefekt:** `ether4` hat `pvid=40` (untagged **rein** → VLAN40 ✓), ist in VLAN40 aber **tagged**-Member (untagged sind `ether2`/`ether3`/`ether5`) → alles **raus** ist 802.1Q-getaggt und der untagged Switch/TV verwirft es. Einseitige Kommunikation.
|
||
- **Belege:** TX 3,4 GB / 16 M Pakete gegen RX 452 KB / 1.338 Pakete, 0 stabil gelernte MACs; TV-Lease `10.40.40.11` (`70:AF:24:B8:C9:86`) **bound, last-seen 1h54m**, gleichzeitig ARP-Status **`failed`** — ein Gerät, das nachweislich sendet, aber nicht antwortet. Der bound-Lease ist kein Gegenbeweis: beim Einschalten macht der TV einen INIT-REBOOT-`REQUEST` auf seine alte IP, RouterOS quittiert mit ACK und markiert bound — **das ACK muss den Client nie erreichen**.
|
||
- **Herkunft:** `ether4` war als **ap1-Uplink-Trunk vorbereitet** („ap1 wandert #15→#4"); ap1 blieb auf `ether15`, in den übrigen Trunk-Port wanderte das Sideboard-Kabel.
|
||
- ✅ **BEHOBEN 2026-08-13 — am Port, nicht am Kabel (User-Vorgabe): „Patchfeld 1–10 ist durchnummeriert und komplett gepatcht, die Nummerierung wird nicht wegen einer Config geändert."** Richtig so — die Portnummer ist physische Infrastruktur. Also `ether4` sauber auf **untagged VLAN40** gezogen statt das Kabel zu verschieben.
|
||
- **Ausgeführt via Nautobot-GC-ConfigPlan** (`plan_type=manual`, 6 Zeilen, change_control_id `TP21.1-4 …`), nicht per Direkt-Write — Governance gewahrt. Vorher `/system backup` = `core-pre-ether4-vlan40-20260813.backup` (Regel 3). `ether4` aus den Tagged-Listen von VLAN 10/20/30/40/50/90 entfernt + als untagged-Member zu VLAN40 ergänzt. **`pvid=40` stand bereits** — das war nie das Fehlende. **Verifiziert:** `vlan40 untagged=ether2,ether3,ether4,ether5`, `tagged=ether1,ether8,ether15`; vlan99 (Mgmt) und vlan100 (WAN) unangetastet. Damit ist auch der Flood-Leak aus IoT/INFRA/MAIN zum Wohnzimmer-Switch weg.
|
||
- ✅ **End-to-End-Beweis ERBRACHT (2026-08-14, live via MikroMCP auf `crs326-core`)** — der TV war am 08-13 tatsächlich nur im Standby. Alle vier Kriterien grün: `ping 10.40.40.11` = **5/5, 0 % Loss** (avg 563 µs); ARP `complete=true`/`status=delay` auf `vlan40` (vorher `failed`); `/interface/bridge/host` lernt `70:AF:24:B8:C9:86` auf **`ether4`, VID 40**; `ether4` `rx-unicast=6.909` / 3,0 MB RX (am 08-13 noch 1.338 Pakete / 452 KB → der Downstream lebt jetzt). DHCP-Lease `tv` weiterhin bound auf `dhcp-v40`. **Damit ist TP21.1 Punkt 4 vollständig abgeschlossen** und die Diagnose „tagged Egress auf einem untagged Port" ist end-to-end bestätigt (SSoT-Merkregel: `ref_tagged_egress_oneway_port`).
|
||
- 🔴 **INCIDENT + Lehre (2026-08-13): Der erste Deploy lief gegen das FALSCHE GERÄT (ap1 statt core).** Ich hatte 4 Device-UUIDs mit einer Sammelabfrage geholt und die **Reihenfolge angenommen**; die Nautobot-REST-API sortiert aber **alphabetisch** → `ap1, ap2, crs326-branch, crs326-core`. Der ConfigPlan trug damit ap1.
|
||
- **Schaden: keiner.** RouterOS wies jedes `set` ab (`invalid value for argument interface`), weil `ether8/13/15` auf einem 5-Port-hAP nicht existieren; die Ablehnung ist **pro Kommando atomar** → kein Teilzustand. ap1 nachgeprüft: VLAN-Tabelle vollständig, 25 WLAN-Clients, Mgmt `.99.11`. **Das war Glück, kein Können** — bei einem auf beiden Geräten *gültigen* Kommando (z.B. `tagged=ether1`) hätte es ap1s VLAN-Tabelle zerlegt.
|
||
- **Regel daraus: Device-UUIDs IMMER einzeln per Name auflösen, nie aus einer Sammelabfrage positional zuordnen** — und vor dem Deploy `plan.device.display` gegen den Zielnamen assert'en (im Skript jetzt so gemacht).
|
||
- **Bestätigt zum zweiten Mal:** der Deploy-**Job** meldete `SUCCESS` und der **Plan** `Completed`, obwohl auf dem Gerät nichts ankam. **Weder Job- noch Plan-Status sind ein Wirksamkeitsnachweis — nur die Gegenprüfung am Gerät zählt.** Der Fehl-Plan wurde gelöscht (irreführender Datensatz).
|
||
- ✅ **Lücke geschlossen (2026-08-13, User: „compliance rules! die Änderung machen wir"):** `/interface bridge port` + `/interface bridge vlan` sind jetzt für **alle drei Geräteklassen** im Template und als ComplianceFeatures scharf — s. TP22.
|
||
5. **`ap1`/`ap2` Neuprovisionierung** (WLAN-Rebuild, eigene Session) — ap1 ist Werksreset; danach greifen die `.99.11`/`.99.12`-Registry-Einträge.
|
||
- ✅ **Nachtrag 2026-08-23 (Doku-Session): die Aufräum-Liste ist vollständig abgearbeitet** — deshalb
|
||
steht TP21.1 jetzt im Archiv. Punkt für Punkt:
|
||
**1** DoH-Gateway ✅ (TP23 Stufe 1, 08-18 — mit `verify-doh-cert=yes`; die Sorge „core hat kein CA"
|
||
war zu pessimistisch, es brauchte **einen** Root, nicht das Bundle) ·
|
||
**2** intended config zurückgeschrieben ✅ (TP22-Re-Baseline 08-18; belegt durch **104/104 compliant,
|
||
0 Drifts**, live gegen die Nautobot-API geprüft am 08-23) ·
|
||
**3** branch-`192.168.88.1`-Rest ✅ **live gegengeprüft am 2026-08-23**: `/ip address print where
|
||
address~"192.168.88"` auf `crs326-branch` liefert **leer** — der Bootstrap-Rest ist weg ·
|
||
**4** `ether4`/Sideboard ✅ (08-13/08-14, s.o.) · **5** AP-Neuprovisionierung ✅ (TP3-Rebuild 08-08,
|
||
seit TP28 08-17 ohnehin überholt — beide APs sind reine CAPs).
|
||
|
||
<a id="tp22"></a>
|
||
|
||
### TP22 — Device-Adoption + Golden-Config Compliance-Push (RouterOS-Fleet) ▸ ✅ **DURCH (2026-08-13)**, Compliance-Backlog **abgearbeitet 2026-08-16 → 68/68, 0 Drifts** — alle 5 Schritte erledigt (Schritt 4 per Entscheid „keine Secret-Injection"); aus TP21 ausgelagert 2026-08-08
|
||
- **Ziel:** die 4 Live-Geräte (crs326-core, crs326-branch, ap1, ap2) real unter **Nautobot Golden Config** nehmen — Backup → Compliance/Drift → intended → **Push (`enable_deploy`)**. Damit greift der Governance-Entscheid „Config exklusiv über Nautobot" operativ. Macht die aktuellen `routeros/.secrets/*.rsc`-Handoffs mittelfristig überflüssig.
|
||
- **Stand aus TP21:** Plattform + Datenmodell + Git-Template + intended-config für core stehen; ap1/ap2 als reine APs zurückgeschrieben (`nautobot/ingest-ap-rebuild.py`, 2026-08-08). `enable_plan`/`enable_deploy` in `nautobot_config.py` noch **bewusst aus** (pull/read-only).
|
||
- ✅ **Schritt 0 DURCH (2026-08-13) — Backup-Scrubbing + Repo-Neuanlage.** Vorgezogen aus Schritt 4, weil ohne Scrubbing jedes weitere Backup Secrets geleakt hätte.
|
||
- 🔴 **Root-Cause-Fund:** Es gab **keine** ConfigReplace/ConfigRemove-Regeln → der Backup-Job vom 2026-07-22 hatte den ap1-`/export` ungefiltert nach Gitea geschrieben. `backups/ap1.cfg` enthielt im Klartext den **damals wie heute live genutzten WG-Hub-private-key** (bei der Core-Recovery unverändert auf `crs326-core` übernommen, damit FM/DW-Spokes matchen) **und 6 WLAN-PSKs**.
|
||
- ✅ **5 ConfigReplace-Regeln** (Platform RouterOS) via `nautobot/ingest-gc-scrubbing.py` (Dry-Run-Default, Regex-Selbsttest bei jedem Lauf): `10-wifi-passphrase`, `20-wireguard-private-key`, `30-preshared-key`, `40-password`, `50-secret` → Maske **`"<<REDACTED>>"`**. Gegen den echten Export kalibriert: RouterOS quotet nur bei Bedarf (4 von 6 PSKs standen **unquotiert**), deshalb Alternation `("(?:[^"\\]|\\.)*"|\S+)` mit der **quotierten Variante zuerst** (`\S+` ist greedy und würde bei einem PSK mit Leerzeichen den Rest exponieren). Replace normalisiert immer auf die quotierte Form → Maske ist formunabhängig byte-identisch (Voraussetzung für das Template in Schritt 1). Negativtest: `public-key=` wird **nicht** maskiert (sonst bräche der WG-Peer-Block).
|
||
- 🐞 **Blocker nebenbei gefunden + gefixt: Nautobot hatte das ALTE Router-Passwort.** Der erste Backup-Lauf meldete Job-`SUCCESS`, schrieb aber nichts — `backup_last_attempt_date` sprang, `backup_last_success_date` nicht (**GC meldet Job-Erfolg auch bei Geräte-Fehlern; nur das Success-Datum je Gerät zählt**). Ursache: `NAUTOBOT_ROUTEROS_PASSWORD` im Stack war 12 Zeichen aus der Zeit vor der PW-Vereinheitlichung (2026-08-08, 32 Zeichen, identisch auf allen 4 MikroTik). Fix = Stack-Env nachgezogen + Stack 203 redeployed; Datei zusätzlich auf `chmod 600` (Backup `.bak-20260813`).
|
||
- ✅ **Gitea-Repo `admin/bf-golden-config` gelöscht + neu angelegt** (User-Entscheid: Repo neu, **nicht** rotieren — History-Rewrite verworfen, weil Gitea unerreichbare Blobs bis zum server-seitigen `gc` behält). Struktur + Template + README wiederhergestellt. **Vollscan aller Blobs der neuen History: 0 unmaskierte Secrets** (die 6 Regex-Treffer sind 5× README-Doku der Feldnamen + 1× der Template-Platzhalter). Nautobot-GitRepository re-synct, End-to-End-Backup ins frische Repo verifiziert (112 Z., 6/6 PSKs maskiert).
|
||
- 📌 **Risiko-Akzeptanz (User 2026-08-13): keine Rotation.** Faktenlage: Repo `private=true`, anonymer Zugriff 404. Aber Gitea hängt auf CF-`bypass` am Internet und `ha-xstate` ist ein **Service-Account mit Gitea-Admin** (kann jedes private Repo lesen) → Lesbarkeit für 2 Accounts, nicht fürs Internet. **Trigger für nachträgliche Rotation:** ändert sich die Gitea-Exposition oder der `ha-xstate`-Account → WG-Key rotieren (FM/DW-Klickanleitung `routeros/fritzbox-wg-lanlan-klickanleitung.md`).
|
||
- ⚠️ **DR-Konsequenz:** ein maskiertes Backup ist **kein Wiederherstellungs-Artefakt**. Restore hängt weiter an `~/.secrets/`, `routeros/.secrets/*` und `routeros/ap1-wg-hub.privkey` (alle gitignored).
|
||
- ✅ **Schritt 1 DURCH (2026-08-13) — AP-Zweig im Golden-Config-Template.**
|
||
- **Datenmodell = ConfigContext** (`nautobot/ingest-ap-configcontext.py`). Verworfen: Wireless-`RadioProfile` (haengt über `ControllerManagedDeviceGroup` an einem *Controller* — den gibt es seit der CAPsMAN-Abschaffung bewusst nicht mehr; ein Fake-Controller wäre Modellschuld), CustomFields (skalieren nicht), Ableitung aus VAP-Descriptions (SSID nur als Freitext → stille Brüche). Angelegt: ConfigContext **`ap-common`** (Scope Role *Wireless AP* + Platform RouterOS, weight 1000) mit `zones`/`radios`/Site-Konstanten + **`local_config_context_data`** je AP mit nur den 2 Kanalnummern. **Frequenz wird im Template gerechnet** (`5000+5*ch` / `2407+5*ch`) statt gespeichert.
|
||
- **Cross-Check im Skript:** die `zones`-Tabelle wird gegen die 11 real modellierten VAP-Interfaces (Name-Präfix + `untagged_vlan`) validiert, Divergenz = Abbruch → ConfigContext und DCIM können nicht auseinanderlaufen.
|
||
- **Template:** neuer Zweig `{% elif obj.role.name == "Wireless AP" %}` (rollen-, nicht namensbasiert). Scope bewusst **nur die compliance-verwalteten Sektionen** (channel/datapath/security/configuration/bridge-vlan/vlan99/ip-address/route) — **nicht** die ganze Config: defconf-Boilerplate (ipv4/ipv6-Firewall, `/disk`, `/tool mac-server`) und die VAP-Zeilen bleiben draussen, weil deren MACs gerätevergeben und in Nautobot nicht modelliert sind. Die früheren „Ausnahmen" fallen aus den Daten heraus: IOT hat `bands:["2g"]` → landet automatisch nur auf `wifi2`; VLAN1/VLAN99 stehen als literale Zeilen ausserhalb der Schleife (**VLAN99 auf `bridge`, NICHT auf die Radios** — sonst Mgmt over-the-air).
|
||
- 🔑 **Formatregel gelernt: gegen den echten `/export` schreiben, nicht gegen die `.rsc`.** RouterOS sortiert Attribute **alphabetisch** und schreibt `ft-over-ds` nur bei `ft=yes`; die handgesetzte `.rsc` hat abweichendes Padding/Reihenfolge. Referenz-Export: `routeros/state-backups/ap1-gc-backup-scrubbed-20260813.cfg`. **RouterOS exportiert CRLF**, Jinja rendert LF — GC normalisiert das korrekt (die schon vorher grüne `system-identity`-Compliance war der Beleg).
|
||
- ✅ **Abnahme:** ap1 **Intended == Geräte-Ist, 0 Drift** (33 Zeilen, zeilenweise identisch); ap2 unterscheidet sich in **exakt den 4 erwarteten Werten** (identity, 5180↔5745, 2412↔2462, `.99.11`↔`.99.12`).
|
||
- ✅ **Mitgenommen:** WG-Privkey-Platzhalter im core-Zweig auf dieselbe Maske `"<<REDACTED>>"` vereinheitlicht (sonst Dauer-Diff, sobald eine Feature auf `/interface wireguard` liegt); **DNS-Statics korrigiert** (s. Nummernplan unten). `routeros/push-gc-template.py` gehärtet: **Pre-Push-Secret-Guard** (verweigert den PUT bei unmaskierten Werten), `--dry-run` mit Diff, `-m` statt hartkodierter Commit-Message, `sha` optional (sonst 404 nach Repo-Neuanlage).
|
||
- ✅ **Schritt 2 DURCH (2026-08-13) — Adoption + Backup/Compliance grün, 20/20.**
|
||
- SecretsGroup **„RouterOS Nornir"** an `crs326-core` + `crs326-branch` gehängt (ap1/ap2 hatten sie schon) — einheitliches Admin-PW seit 2026-08-08 macht eine Gruppe für alle 4 ausreichend.
|
||
- **Datenmodell-Korrekturen vorgezogen (technisch Voraussetzung):** `crs326-core` Status *Planned* → **Active** (ist der produktive L3-Router), `crs326-branch` `primary_ip4` `.99.13` → **`.99.2`** (live verifiziert).
|
||
- ✅ **Backup über alle 4 erfolgreich**, Sichtprüfung: **0 unmaskierte Secrets** (ap1/ap2 je 6 PSKs maskiert, core WG-Privkey maskiert, branch secret-frei).
|
||
- ✅ **4 neue ComplianceFeatures** (`wifi-channel`, `wifi-datapath`, `wifi-security`, `wifi-configuration`, alle `config_ordered=false`) + die bestehende `system-identity` → **20/20 compliant** über alle 4 Geräte.
|
||
- ⚠️ **Bewusst noch KEINE Features für `bridge-vlan`/`ip-address`/`ip-route`:** ComplianceRules gelten pro *Platform*, nicht pro Rolle — und der **`crs326-core`-Zweig ist in Slash-Form** geschrieben (`/ip/address add …`), was den Export (Space-Form, `/ip address add …`) **nicht** matcht → das gäbe permanenten Falsch-Drift auf dem Core. **Follow-up: core-Zweig auf Export-Form umstellen**, dann diese Features nachziehen. Die wifi-Features sind unkritisch, weil core/branch keine WLAN-Hardware haben (beide Seiten leer = compliant).
|
||
- ✅ **Follow-up DURCH (2026-08-13) — `crs326-core`-Zweig auf `/export`-Form.** Der Zweig war als *staged Cutover-Skript* geschrieben (Slash-Form `/ip/address`, plus `disabled=yes`-Flags aus der Vorbereitungszeit) und matchte den Export nicht → er hätte platform-weite ComplianceRules dauerhaft rot gefärbt. Jetzt Space-Form + alphabetische Attribute, `disabled=yes` entfernt (core ist seit 2026-08-08 produktiv), VLAN-Tabelle als Jinja-Konstante, GW-IPs weiter datengetrieben. **Ergebnis: 68 von 69 Zeilen byte-identisch zum Gerät.** Zusätzlich **Switch-Zweig** für `crs326-branch` (Mgmt-Layer) ergänzt — vorher fiel branch auf „nur `/system identity`" durch und meldete legitime Config als Drift. **9 weitere ComplianceFeatures** (interface-list/-vlan/-wireguard, ip-address/-pool/-dhcp-server/-dns/-firewall-nat/-route); bei `ip-dhcp-server` bewusst **drei spezifische Präfixe** statt `/ip dhcp-server`, sonst fingen die 54 dynamischen `lease`-Zeilen mit.
|
||
- ✅ **Schritt 3 DURCH (2026-08-13) — Deploy-Pipeline scharf, Pilot erfolgreich.**
|
||
- Image **`3.1.7-gc9`** auf der NAS gebaut (`/volume1/docker/nautobot-image`), Stack 203 retaggt. ⚠️ **Falle:** ein bereits vorhandenes `gc7` trug dieselben Flags; ein Build ohne geänderte Quelldatei zieht den **Docker-Cache** und liefert eine identische Image-ID — immer gegen die Flags im *fertigen* Image verifizieren. `scp` auf die NAS schlug fehl („Connection closed"), Übertragung per `ssh 'cat > datei'`.
|
||
- **Jobs sind in Nautobot zusätzlich einzeln freizuschalten** (`enabled=true`): die Plugin-Flags machen sie nur verfügbar.
|
||
- 🔴 **Kernbefund: `napalm-ros` ist ein READ-ONLY-Treiber.** Der Default-Dispatcher wählt NAPALM (weil `Platform.network_driver_mappings` `napalm: ros` enthält) → Backup (`get_config`) geht, **`merge_config` scheitert grundsätzlich**, mit leerer Fehlermeldung (`E1015 … ``). **Fix:** in den Golden-Config-Settings (nicht in den Nornir-Settings!) `custom_dispatcher: {"mikrotik_routeros": "nornir_nautobot.plugins.tasks.dispatcher.mikrotik_routeros.NetmikoMikrotikRouteros"}` — dieser Dispatcher pusht via `netmiko_send_config`. Danach lief der Deploy sofort.
|
||
- ⚠️ **Gescheiterter Deploy = sauberer Fehlschlag:** das Gerät blieb beim NAPALM-Versuch vollständig unverändert (kein Teilzustand). Zusätzlich meldet der Deploy-**Job** `SUCCESS`, obwohl der **Plan** auf `Failed` steht — wie beim Backup gilt: **nur der Objekt-Status zählt, nicht der Job-Status.**
|
||
- ✅ **Pilot-Deploy: die DNS-Statics auf dem Core.** Vorher `/system backup` (`core-pre-tp22-first-deploy-20260813.backup`). ⚠️ **`config_set` eines generierten Plans ist schreibgeschützt**, und `plan_type=missing` erzeugt **nur `add`** — das hätte Duplikate ergeben (`ap1.bf.lan` gleichzeitig auf `.99.3` und `.99.11`). Deshalb **manueller Plan mit Purge-vor-Add**. Ergebnis verifiziert: `ap1→.99.11`, `ap2→.99.12`, `branch→.99.2`, weiterhin 11 Einträge, keine Duplikate. **Compliance 51/56.**
|
||
- ✅ **Schritt 5 DURCH (2026-08-13) — Datenmodell-Nachträge, `nautobot/ingest-tp22-datamodell.py`.** Quelle war durchgehend **das Gerät**, nicht die Doku: `bridge/port` (pvid), `bridge/vlan` (tagged/untagged), `bridge/host` (gelernte MACs je Port+VID) plus ARP/DHCP-Leases zur Identifikation der Gegenstellen.
|
||
- **`crs326-core`: von 5 auf 26 Ports.** Vorher waren 3 davon als „GEPLANT (tentativ)" modelliert und `ether1` zeigte auf das tote `sw1 #12`. Belegt (MAC-hart): `ether8`→crs326-branch, `ether13`→pve `.20.100`, `ether14`→Sonoff `.30.250`, `ether15`→ap1, `ether16`→Arris/WAN, `ether5`/`ether6`→Sonos Schlafzimmer `.195`/`.193`. Die Trunk-Sets sind **exakt wie am Gerät** übernommen, inkl. Schiefstände (z.B. trägt `ether1` kein VLAN20/99) — bewusst nicht geradegezogen.
|
||
- **`crs326-branch`:** `ether1`-Ziel `sw1 #8` → `crs326-core ether8`; `ether5`/`ether6` = Sonos Arbeitszimmer (aktuell kein Link, die Schlafzimmer-Paare hängen am Core); `ether9–23`+sfp waren als „disabled" modelliert, sind live aber **enabled** (Bootstrap Rev4) → korrigiert.
|
||
- **Standort-Fehler gefunden:** `crs326-branch` **und** `ap2` standen in Nautobot auf *Technik UG*, real *Technik OG* (die Doku war richtig, das Modell falsch) → korrigiert.
|
||
- **Status-Endzustände:** sw1/sw2/cudy2 → **Decommissioned** (physisch ausgebaut; „Decommissioning" heißt *Prozess läuft*). Nautobot liefert diesen Status mit, aber nur für `circuits.circuit` → `content_types` um `dcim.device` erweitert statt einen zweiten gleichnamigen anzulegen. `cudy1` → **Offline** (core `ether11` hat kein Link; der endgültige Teardown bleibt TP19). sw1s 4 Duplikat-Ports (`gi8/gi11/gi15/gi16` neben `GE*`) entfernt.
|
||
- ✅ **Compliance-neutral verifiziert:** Generate-Intended + Compliance über alle 4 Geräte neu gelaufen → weiterhin **51/56** mit exakt denselben 5 bekannten Drifts. Grund: der core-Zweig des Templates iteriert nur Interfaces mit Präfix `vlan` und hält die Bridge-VLAN-Tabelle als Jinja-Konstante. **Umkehrschluss als Dauerregel: die `vlanXX`-Descriptions auf dem Core sind NICHT kosmetisch** — sie landen als `comment=` in der intended config.
|
||
- 🔍 **Nebenbefund, halbkonfigurierte Ports (NICHT angefasst, gehört zu TP19/TP5):** `core ether11` (cudy1) hat `pvid=10` ohne VLAN10-Membership → funktionslos; `core ether3` hat `pvid=1` bei untagged-VLAN40-Membership; `core ether2` ist gleichzeitig statisches untagged-Member von VLAN1 **und** VLAN40; `branch ether9–23` tragen `pvid=20` ohne jede Membership. Alles Bring-up-Reste, betriebsneutral (kein Link), aber sie gehören in die VLAN1-Aufräumrunde.
|
||
- ✅ **Nachtrag 2026-08-13 — Compliance-Abdeckung erweitert auf 68 Checks (63/68 grün; seit 2026-08-16 **68/68**).** Drei neue ComplianceFeatures, alle auf Anhieb grün:
|
||
- **`wifi-steering`** (`/interface wifi steering`) — sichert die TP3.3-Reparatur ab.
|
||
- **`bridge-port`** + **`bridge-vlan`** (`/interface bridge port` / `/interface bridge vlan`) — der **L2-Layer war bis dahin komplett unüberwacht**: der core-Zweig emittierte gar keine Bridge-Sektion, der AP-Zweig nur die VLAN-Tabelle ohne Ports. Jetzt für alle drei Geräteklassen im Template.
|
||
- 🔑 **Bewusst als Literal-Block, nicht aus DCIM abgeleitet.** Der As-built trägt akzeptierte Schiefstände (`core ether3` ist untagged-Member von VLAN40, hat aber `pvid=1`; `core ether2` hängt zusätzlich in VLAN1; `branch ether9–23` haben `pvid=20` ohne Membership). Würde man `pvid` aus `untagged_vlan` generieren, meldete die Compliance genau diese bekannten Altlasten als Dauerdrift. Der Literal-Block liefert das, was hier gebraucht wird: **Drift-Erkennung gegen den Ist-Zustand**.
|
||
- ⚠️ **`/interface bridge` selbst bleibt außen vor** — trägt die gerätespezifische `admin-mac`, wäre pro Gerät verschieden und ist kein Fleet-Standard.
|
||
- 🐞 **Falle dabei: Compliance vergleicht gegen das letzte BACKUP, nicht gegen das Live-Gerät.** Nach dem Steering-Deploy meldete sie erst Drift auf `wifi-configuration`/`wifi-steering`, weil das Backup vom Nachmittag stammte. **Reihenfolge nach jeder Geräteänderung: Backup → Intended → Compliance.**
|
||
- ✅ **Compliance-Backlog ABGEARBEITET (2026-08-16, User-Auftrag „beseitige autonom alle Compliance-Drifts") — Ergebnis: 68/68, 0 Drifts.**
|
||
- **Was entfernt wurde (alle vom Typ `extra`, also am Gerät vorhanden und in der intended config bewusst nicht vorgesehen):** ap1+ap2 `/ip dns static 192.168.88.1 router.lan` (Werks-`defconf`, zeigte auf eine IP, die es im Netz nicht gibt — der Core hat sein eigenes, korrektes `router.lan → 10.40.99.1`); `crs326-branch` `vlan10 "TEMP Mgmt VLAN10"` + `10.40.10.2` (Bring-up-Interim). Der fünfte Drift, `crs326-core` `10.40.41.211`, war schon vorher über den ConfigPlan `TP19-vlan1-core-address` gefallen.
|
||
- **Richtung der Beseitigung bewusst „am Gerät entfernen", nicht „ins Template aufnehmen"** — das entspricht der Absicht, die im Switch-Zweig von `mikrotik_routeros.j2` seit 2026-08-13 schriftlich steht („Bewusst NICHT emittiert … gehoeren entfernt"). Die frühere Zurückhaltung („potenzielle Rettungspfade") ist mit dem User-Entscheid vom 2026-08-16 aufgehoben; der Mgmt-Fallback aus VLAN10 entfällt damit, Rückweg im Notfall = MAC-Winbox am selben Draht.
|
||
- **Weg: 3 manuelle GC-ConfigPlans** (`TP22-drift-aps-defconf-dns`, `TP22-drift-branch-temp-vlan10`), Governance gewahrt — kein Direkt-Write. Vorher `/system backup` je Gerät (`{ap1,ap2,branch}-pre-drift-cleanup-20260816.backup`). Deploy **gestaffelt**: erst die beiden APs, am Gerät gegengeprüft, dann branch.
|
||
- 🔑 **Neue Falle gelernt: `POST /plugins/golden-config/config-plan/` ist `405 Method Not Allowed`** — das ViewSet ist read-only. ConfigPlans entstehen **ausschließlich** über den Job **`Generate Config Plans`** (bei `plan_type=manual` landet der `commands`-TextVar als `config_set` auf jedem ausgewählten Gerät). Das erklärt, warum es bis dahin kein Skript gab und alle 17 Vorgänger-Pläne per Hand entstanden sind.
|
||
- ✅ **Verifikation am Gerät, nicht am Job:** ap1/ap2 `list_dns_entries` → `total: 0` bei unveränderten 28 bzw. 3 WLAN-Clients; branch `/interface vlan` → nur `vlan99`, `/ip address` → nur `10.40.99.2/24`, `ping` vom Core 4/4 mit 0 % Loss. **`/interface bridge vlan/export` byte-identisch** zu vorher — die Gegenprobe darauf, dass kein neuer Drift entsteht (`bridge` steht dort statisch als tagged-Member von VLAN10, unabhängig vom SVI). Danach `Backup → Intended → Compliance`, alle 4 Geräte mit frischem `*_last_success_date`.
|
||
- 🛠️ **Tooling neu: `nautobot/gc-configplan.py`** — legt manuelle Pläne über den Job an, löst Device-UUIDs **einzeln per Name** auf und assertet `plan.device.display` **und** `config_set` gegen das Ziel (die alphabetische Sortier-Falle aus TP21.1 kann damit nicht wiederkommen), verweigert jedes `config_set` mit `<<REDACTED>>` (Regel 1) und jede `print`-Zeile, kann gestaffelt deployen (`--deploy-ids`) und kapselt die Reihenfolge `Backup → Intended → Compliance` als `--refresh-compliance`. Dry-Run ist Default.
|
||
- 🔒 **GOVERNANCE — verbindliche Deploy-Regeln (festgeschrieben 2026-08-13, VOR dem Scharfschalten von `enable_deploy`):**
|
||
1. **Config Plans werden ausschliesslich feature-/manual-scoped erzeugt, NIEMALS als „full intended".** Ein Voll-Push schriebe wörtlich `passphrase="<<REDACTED>>"` auf die APs (= alle WLAN-Clients raus, inkl. ~26 Shellys) bzw. `private-key="<<REDACTED>>"` auf den Core (= WG-Hub tot, FM/DW-Spokes weg). **Jede Sektion, die `<<REDACTED>>` enthält (`/interface wifi security`, `/interface wireguard`), ist vom Push generell ausgeschlossen.**
|
||
2. **Das AP-Template wird NIE zeilenweise auf ein laufendes Gerät gepusht.** Die Provisionierung setzt zwischenzeitlich `vlan-filtering=no` → das reisst den vlan99-Mgmt-Pfad ab, die restlichen Zeilen kämen über dieselbe SSH-Session nie an (halb konfigurierter AP). Voll-Reprovisionierung ausschliesslich per Datei-Upload + `/import` (`routeros/.secrets/ap{1,2}-prov-base.rsc`). Verschärfend: `device-mode=home` blockt auf den hAPs `scheduler`/`fetch` → die übliche Auto-Revert-Absicherung steht dort **nicht** zur Verfügung; der VLAN1-untagged-Rettungspfad darf nie wegoptimiert werden.
|
||
3. **Vor jedem Push ein `/system backup` auf dem Zielgerät.** Das GC-Backup ist wegen des Scrubbings **kein** vollwertiges Restore-Artefakt.
|
||
- ✅ **Schritt 4 ENTSCHIEDEN (User 2026-08-13): Variante (a) — keine Secret-Injection, bleibt wie es ist.** Ursprünglich geplant war „WLAN-PSKs + WG-Privkey via Nornir-Secrets injizieren"; die **Governance-Regel 1** (unten) hat das überholt: Sektionen mit `<<REDACTED>>` sind vom Push generell ausgeschlossen, die Secrets werden also nie gebraucht. **Konsequenz, bewusst akzeptiert:** Secrets bleiben dauerhaft außerhalb von Modell und Push, DR läuft weiter über `~/.secrets/` + `routeros/.secrets/*` + `routeros/ap1-wg-hub.privkey`; „Gerät vollständig aus dem Modell wiederherstellbar" gilt nur *ohne* Secrets. Die verworfene Alternative (b) hätte Regel 1 aufweichen müssen — ein Render-Fehler hätte dann WLAN bzw. WG-Hub abgeschossen. **Damit ist TP22 inhaltlich abgeschlossen.**
|
||
- **Schritte:** (1) **AP-GC-Template**: `mikrotik_routeros.j2` um `role == "Wireless AP"`-Zweig erweitern (Bridge/vlan-filtering/ether1-Trunk/vlan99-Mgmt/6 SSIDs **ohne** PSK/L3) → APs komplett aus GC generierbar; (2) **Nornir/NAPALM-Adoption** aller 4 (SecretsGroup „RouterOS Nornir"; **einheitliches Admin-PW seit 2026-08-08 vereinfacht das**), Backup/Compliance-Job grün; (3) **`enable_plan`+`enable_deploy`** scharf — erst Dry-Run/Plan, dann kontrollierter Push; (4) **Secret-Handling** WG-Privkey (core) + WLAN-PSKs (bleiben aus dem Git-Template, via Nornir-Secrets injizieren); (5) Datenmodell-Nachträge: branch/core-Ports final, sw1 (außer Betrieb) + cudy1-Status.
|
||
- **Nutzen:** Drift-Erkennung + reproduzierbarer Rebuild jedes Geräts aus dem Modell.
|
||
- **Abh.:** TP21 ✅ (Plattform/Modell/Templates), TP20 ✅, einheitliches Admin-PW ✅. **Danach ist der kritische Pfad frei für TP5 (Deny-by-default) → TP6 (CAKE).**
|
||
|
||
- ✅ **Nachtrag 2026-08-23 — Governance-Lücke geschlossen: ComplianceRule `wifi-access-list`.**
|
||
`/interface wifi access-list` stand unter **keiner** Regel: Einträge dort erzeugten keinen Drift
|
||
und ließen sich nicht als Transaktion führen (`gc-transaktion.py` verlangt eine ComplianceRule
|
||
als `--scope`). Seit `docs/tp25-pin-s22.sh` schreibt jeder Messrundgang dort Regeln, und **TP25
|
||
Fund E** (`signal-range`-Boden) würde dauerhaft genau dort landen.
|
||
Angelegt per `nautobot/ingest-wifi-access-list-rule.py` (idempotent), Template um den
|
||
dokumentierten Abschnitt **[6c]** ergänzt (**nur Kommentare** — der Soll-Zustand ist *leer*,
|
||
und das ist die Aussage).
|
||
- 🔢 **Zählerkorrektur: 92 → 96, nicht 94.** Die Regeln gelten **plattformweit**, nicht
|
||
gerätespezifisch: 24 Rules × 4 Geräte. Die Annahme „nur 2 Geräte betroffen" war falsch.
|
||
- ✅ **Wirksamkeit bewiesen, nicht behauptet** (die TP22-Regel „Job-Status ist kein
|
||
Wirksamkeitsnachweis"): Test-Eintrag mit einer nicht existierenden MAC gesetzt →
|
||
**95/96 mit exakt dieser Zeile als `extra`** → Eintrag entfernt → **96/96, 0 Drifts**.
|
||
Zusätzlich geprüft: `gc-transaktion.py` akzeptiert `--scope wifi-access-list` jetzt.
|
||
- 🔎 **Nebenbefund beim Start:** ein `bridge-vlan`-Drift auf `crs326-core` stand noch in der
|
||
Anzeige (ether3 als Trunk), obwohl der Sideboard-Rollback am 08-22 gelandet war. Das Gerät
|
||
war live korrekt; die **Compliance vergleicht gegen das letzte Backup**, und das war älter
|
||
als der Rollback. Mit dem Refresh verschwunden. Merkregel bleibt: bei einem Drift zuerst
|
||
fragen, wie alt das Backup ist.
|
||
|
||
<a id="tp26"></a>
|
||
|
||
### TP26 — `/ip firewall filter` unter Drift-Kontrolle ▸ ✅ **DURCH (2026-08-17)**
|
||
> **Nachgetragen 2026-08-18.** TP26 existierte bis dahin nur als zwei Commits und eine `.rsc` — ohne
|
||
> Abschnitt in dieser Datei. Genau die Lücke, die eine frische Session nicht finden kann.
|
||
|
||
- **Auslöser:** vor TP26 gab es eine ComplianceRule auf `/ip firewall nat`, aber **keine** auf
|
||
`/ip firewall filter`. Die vielzitierte „72/72 grün" sagte über die Firewall damit **gar nichts** —
|
||
und das ausgerechnet, während TP5 den Deny-by-default vorbereitete. Der Drop wäre die einzige Regel
|
||
im Netz gewesen, die niemand überwacht.
|
||
- ✅ **Rule `ip-firewall-filter` angelegt**, `config_ordered=true` — bei Firewallregeln ist die
|
||
Reihenfolge semantisch, nicht kosmetisch. Ergebnis damals: **76/76**. Damit war TP5 Etappe 3 entblockt.
|
||
- ✅ **Zweiter Teil: die defconf-Firewall auf beiden APs abgeräumt** (`routeros/tp26-ap-firewall-cleanup.rsc`).
|
||
ap1/ap2 trugen je 12 Werksregeln — input-seal, fasttrack, ipsec-Zeilen, sogar „accept to local loopback
|
||
(for CAPsMAN)". Inert, weil ein reiner AP nichts routet. **Entscheid: abräumen statt ins Template
|
||
schreiben** — sonst erklärt man Werksmüll zur Absicht. Dasselbe Argument hat 2026-08-18 auch die alten
|
||
WLAN-Objekte auf den APs erledigt (TP28).
|
||
- 📌 **`--assert` hat dabei zwei Fehlschläge gefangen**, die der Job-Status als Erfolg gemeldet hätte.
|
||
Seitdem ist die Gegenprüfung am Gerät fester Bestandteil von `gc-manual-plan.py`.
|
||
- **Abh.:** TP22 ✅. Direkte Folge: TP5 E3/E4.
|
||
|
||
<a id="tp27"></a>
|
||
|
||
### TP27 — RouterBOOT-Firmware auf 7.24 nachziehen ▸ ✅ **DURCH (2026-08-17/18)**
|
||
> **Nachgetragen 2026-08-18.** Auch TP27 hatte keinen Abschnitt — die Arbeit steckt in Christophs Commit
|
||
> `b93514a` plus `routeros/tp27-routerboot-firmware-724.rsc` und
|
||
> `docs/routerboot-firmware-klickanleitung.md`.
|
||
|
||
- **Kontext:** Christoph hat alle vier MikroTik selbst auf **RouterOS 7.24 stable** gehoben
|
||
(core/branch 7.19.5 → 7.24, ap1/ap2 7.23.2 → 7.24). Offen blieb die zweite Hälfte jedes
|
||
MikroTik-Upgrades: die **RouterBOOT-Firmware** hängt danach zurück.
|
||
- 🔑 **Die Falle, wegen der es ein eigener TP ist:** `/system routerboard upgrade` **schreibt** die
|
||
Firmware nur — wirksam wird sie erst beim **nächsten Reboot**, und `current-firmware` zeigt bis dahin
|
||
weiter den alten Wert. Wer direkt nach dem Befehl prüft, hält den Schritt fälschlich für gescheitert.
|
||
Reihenfolge also: schreiben → gezielt rebooten → `current-firmware` gegenprüfen.
|
||
- ⚠️ **`/system routerboard upgrade*` steht auf der MikroMCP-Deny-Liste** (zu Recht — es schreibt den
|
||
Bootloader). Der Weg führt über Golden Config; der Reboot dann über `sshpass`/MikroMCP.
|
||
- ✅ **Abnahme 2026-08-18:** auf allen vier Geräten `current-firmware = upgrade-firmware = 7.24`.
|
||
- **Abh.:** keine. Berührt TP28 insofern, als CAPsMAN erst ab 7.2x das neue `/interface/wifi` mitbringt.
|
||
|
||
<a id="tp28"></a>
|
||
|
||
### TP28 — WLAN über CAPsMAN auf `crs326-core` ▸ ↩️ **WIEDER GEÖFFNET 2026-08-30**
|
||
|
||
> Der Volltext steht seit dem 2026-08-30 wieder in
|
||
> [`teilprojekte.md` → TP28](teilprojekte.md#tp28), weil mit **ap3** (cAP ax) ein dritter
|
||
> Access-Point dazukommt. **Ein archiviertes TP ist ein geschlossenes Ticket — es darf wieder
|
||
> geöffnet werden** (Christoph-Direktive 2026-08-30); der Abschnitt wandert zurück, nicht nur
|
||
> ein Nachtrag. Hier bleibt dieser Zeiger stehen, damit alte Links nicht ins Leere gehen.
|
||
|
||
<a id="tp-r"></a>
|
||
|
||
### TP-R — Router-Capabilities-Sweep ▸ FERTIG (`docs/research/routeros-capabilities.md`)
|
||
- **Leitsatz: nativ vor Container, NAS vor Router** (128MB-Flash-Limit).
|
||
- **Sofort nativ (~0 RAM):** RoMON · Netwatch · Scheduler + Auto-Backup/Mail · SNMP→externes Monitoring · **mDNS-Repeater** · spectral-scan · **802.11r/k/v (Fast-Roaming statt Mesh)** *(die Sweep-Fassung nannte hier CAPsMAN — das ist seit 2026-08-08 abgeschafft; 11r/k/v läuft standalone weiter, s. TP3/TP3.3)* · Back-to-Home/WireGuard.
|
||
- **Auf NAS:** FreeRADIUS(+LDAP) · NPM · icinga/Grafana · CF-Tunnel.
|
||
- **Weglassen:** LoRa (keine HW) · OpenFlow · CALEA · **ZeroTier (CPU-Fresser bis ~60 % → WireGuard)** · lokales `dude`.
|
||
- ✏️ **Korrektur Reverse-Proxy:** RouterOS **7.22+ HAT** einen nativen (`/ip/reverse-proxy`, ACME/SNI) — aber **ersetzt NPM NICHT** (kein WebSocket/gRPC/Sticky, nur IPv4-Backends, TLS frisst Router-CPU) → NPM bleibt auf NAS.
|
||
- Speist TP8/9/11/12.
|
||
|
||
---
|
||
|
||
---
|
||
|
||
<a id="tp25-lang"></a>
|
||
|
||
## TP25 — Langfassung (Messchronik + Funde im Volltext, Stand 2026-08-22)
|
||
|
||
> ⚠️ **TP25 ist NICHT abgeschlossen.** Der geltende Stand, das Offene (Fund F, die
|
||
> 20-MHz-Entscheidung) und die Merkregeln stehen in [`teilprojekte.md` → TP25](teilprojekte.md).
|
||
> Hier liegt **wortgleich** die Fassung, die bis zum 2026-08-23 dort stand — 394 Zeilen Herleitung:
|
||
> die Messchronik (A)–(G) mit allen zwischendurch korrigierten Irrtümern, die Funde A–F im Volltext,
|
||
> die beantworteten Fragen (Beobachtung 5 „CAPsMAN oder Nautobot GC?", Herkunft der ch149-Notiz,
|
||
> `2ghz-g` für IoT, Deckenmontage-APs) und die Umsetzungsreihenfolge mit den Deploy-Gotchas.
|
||
>
|
||
> 📌 **Warum das nicht gelöscht wurde:** ein guter Teil des Werts steckt in den *widerlegten*
|
||
> Erklärungen — zweimal stand hier eine plausible Ursache, die die nächste Messung gekippt hat
|
||
> (ETSI-Band C bei Fund B; „ap1 ans Sideboard" bei (F)/(G)). Wer TP25 fortsetzt, muss sehen können,
|
||
> welche Wege schon abgelaufen sind.
|
||
### TP25 — WLAN-Stabilisierung ▸ 🟢 **KERN ERLEDIGT (2026-08-22): Dead Spot Terrasse repariert** (43 % → 9 % Verlust, 20,4 s → 1,0 s). Offen nur noch **Fund F** (5:05-Takt IoT) und die Entscheidung **5 GHz auf 20 MHz**. Nicht mehr kritischer Pfad — Sammel-TP für Christophs Beobachtungen 1–6
|
||
> 🆕 **Messung 2026-08-20/21: [`tp25-wlan-roaming-messung-20260820.md`](tp25-wlan-roaming-messung-20260820.md)**
|
||
> (Nebenprodukt der TP6-Messreihen, Core-Sampler im 2-s-Takt über 2,5 h, teils mit laufendem Videocall).
|
||
> Drei Befunde:
|
||
> **(A)** Der Bandabstand 2,4 ↔ 5 GHz beträgt **8–10 dB** — physikalisch normal (6,7 dB allein
|
||
> Freiraumdämpfung), **keine** Konfigurationsursache. Das Problem ist der Absolutwert: 5 GHz liegt an
|
||
> den Telefonier-Orten bei **−75 bis −80 dBm**.
|
||
> **(B)** **Sticky Client:** ein Weg Küche → Kinderzimmer → Schlafzimmer → Terrasse, dreimal direkt an
|
||
> ap1 vorbei (−43 dBm), **eine einzige Übergabe** — und die erst oben bei −73 dBm.
|
||
> **(C)** **Band-Ping-Pong: 12 Wechsel in 9 Minuten** am selben Ort, immer ap2 5↔2,4 GHz.
|
||
> **(D) 2026-08-21 nachgemessen** (Gehweg, 5 Pings/s auf das Telefon, `docs/tp25-walk-messung.sh`):
|
||
> die „~4 s je Wechsel" waren **Zählerartefakt** — bei **−48…−55 dBm** kostet eine Übergabe
|
||
> **0 bis 2,6 s**, meist unter einer halben Sekunde, **Fast Transition funktioniert also**. Der wahre
|
||
> Befund ist schlimmer und liegt woanders: bei **−69…−77 dBm** ist das Telefon **6 bis 45 s** nicht
|
||
> erreichbar, zweimal über 20 s am Stück. **Nicht die Übergabe ist teuer, sondern die schwache Zone.**
|
||
> **(E) 2026-08-21, Raumkarte — der Dead Spot hat einen Namen: die TERRASSE.** Ping vom Core, Konsole
|
||
> auf dem iPhone, Raumwechsel per Klick markiert. **Im ganzen Haus null Verlust** (Technik UG/OG, Diele,
|
||
> Kinder-, Schlaf-, Arbeitszimmer, Treppe — alle 0 %, Übergaben ap1↔ap2 kosten 0,2 s). **Auf der
|
||
> Terrasse: 43 % Verlust, längste Lücke 20,4 s bei −72 dBm.** Erklärung ist die Topologie: ap2 sitzt in
|
||
> **Technik OG** — das ist aber **kein eigener Raum, sondern ein virtueller Bereich im Arbeitszimmer**
|
||
> (User-Korrektur 2026-08-21, die frühere „drei Wände"-Erklärung war falsch). Zwischen ap2 und Terrasse
|
||
> liegen **zwei Mauern der Diele OG plus teilweise die PV-Anlage an der Mauer** — und die ist als
|
||
> leitende Fläche ein **Schirm**, keine dämpfende Wand. Deshalb wird mehr Leistung oder ein schmalerer
|
||
> Kanal dort wenig ausrichten; gegen einen Schirm hilft nur, ihn zu umgehen. Details: [`tp25-wlan-roaming-messung-20260820.md`](tp25-wlan-roaming-messung-20260820.md) §6.
|
||
> ⚠️ **Konsequenz für Fund E: ein `signal-range`-Boden würde die Terrasse verschlechtern**, weil es dort
|
||
> keinen besseren AP gibt, auf den gesteuert werden könnte. Stattdessen: **(1)** 5 GHz auf 20 MHz
|
||
> (+3 dB, Ein-Wort-Change im Template), **(2)** **ap2 von Technik OG in die Diele OG umhängen** — die
|
||
> grenzt an Terrasse, Arbeitszimmer und Treppe, kostet **keinen neuen AP** und erfüllt damit die
|
||
> User-Randbedingung; zu klären ist die Verkabelung (ap2 hängt an `crs326-branch ether24`).
|
||
> **(3)** dritter AP erst, wenn beides nicht reicht.
|
||
> **(F) 2026-08-21 abends, Raumkarte 2 — ap1 versetzt ans Sideboard (`core ether3`).** Das OG gewinnt
|
||
> (Diele OG −50 ⌀ über 5,5 min mit 0 % Verlust, Technik OG −44), die **Schlafräume verlieren**:
|
||
> Schlafzimmer von −61 (ap1/5 GHz) auf **−83 (ap2/2,4 GHz)**, Kinderzimmer von −53 auf −75, Bad −83 mit
|
||
> 31 % Verlust. 🔴 **Und genau das ist vermutlich KEIN Abdeckungsproblem:** laut User liegen Schlaf- und
|
||
> Kinderzimmer **2 bzw. 4 m Luftlinie von ap1**, während ap2 4–6 m entfernt ist und zwei zusätzliche
|
||
> Mauern dazwischen hat. Ein Client, der so am ferneren AP klebt, ist ein **Steering**-Fall — und damit
|
||
> die erste Stelle im Haus, an der **Fund E (`signal-range`-Boden) wirklich hilft**, weil es hier eine
|
||
> bessere Alternative gibt. **Test steht aus** (Handover §5). Korrektur zur Terrasse: der Platz, an dem
|
||
> das Telefon normalerweise steht, ist mit **−46…−66 dBm gut**; schlecht ist der Teil dahinter.
|
||
> **(G) 2026-08-22 — Sideboard-Test BEENDET, ap1 geht zurück auf `core ether15`.** Ergebnis nach einem
|
||
> Tag, User-Befund: **ap1 am Sideboard richtet das Wohnzimmer nicht** — womit die RF-Vorhersage aus der
|
||
> Idee von 2026-08-13 (geschlossenes Media-Möbel, Metallgehäuse, niedrig) bestätigt ist — **und der
|
||
> Preis ist ein dunkles Schlafzimmer** (Fund F: −61 → −83 dBm). Damit ist die Idee **„ap1 ersetzt den
|
||
> Sideboard-Switch" entschieden: nein.** Die User-Vorgabe „Platzierung hat Vorrang vor dem eingesparten
|
||
> Gerät" greift — und die bessere Platzierung ist die alte. Transaktion
|
||
> `TEST-ether3-trunk-ap1-sideboard` wird zurückgerollt (`ether3` wieder **untagged VLAN40** =
|
||
> Sideboard-Access-Port); Compliance geht damit auf 92/92 zurück.
|
||
> 🔴 **Reihenfolge beim Rückbau:** erst ap1 physisch auf `ether15` umstecken, **dann** die Config
|
||
> zurückrollen. Umgekehrt hängt ap1 im Moment des Deploys ungetaggt in MEDIA und verliert vlan99/Manager.
|
||
> `ether15` ist durchgehend in allen VLANs getaggt geblieben, das Umstecken allein genügt also.
|
||
> ↗️ **Stattdessen als Nächstes:** ap2 wurde auf **2,5 m Höhe** gehievt (User 2026-08-22) — der billigste
|
||
> verbliebene Hebel unter der Randbedingung „kein neuer AP, solange nicht alles aus den vorhandenen
|
||
> rausgeholt ist". Wirkung **noch ungemessen**: Raumkarte mit [`tp25-walk-messung.sh`](tp25-walk-messung.sh)
|
||
> neu laufen, sonst steht Höhengewinn gegen Fund F ohne Zahlen im Raum. **Erster Eindruck des Users
|
||
> (2026-08-22): die Terrasse ist damit besser ausgeleuchtet** — subjektiv, Nachmessung steht aus und
|
||
> ist genau der Punkt, an dem der Dead Spot aus (E)/(F) zu prüfen wäre.
|
||
> ⚠️ **Korrektur 2026-08-22 (User):** die Formulierung „der Steering-Fall entfällt mit dem Rollback" war
|
||
> **falsch**. Was entfällt, ist nur die *konkrete Ausprägung* aus (F) — Schlaf-/Kinderzimmer bei 2 m zu
|
||
> ap1 an ap2 klebend. **Der Defekt selbst bleibt**, er verlagert sich nur: Übergabe bzw. Abriss passieren
|
||
> dann an anderen Stellen im Haus. Das ist auch belegt: **Fund B** (Sticky Client, dreimal an ap1 bei
|
||
> −43 dBm vorbei, eine einzige Übergabe) und **Fund C** (Band-Ping-Pong, 12 Wechsel in 9 min) wurden am
|
||
> **2026-08-20 gemessen — mit ap1 auf `ether15`**, also in genau der Stellung, in die wir jetzt
|
||
> zurückgebaut haben. Der Sticky-Client-Defekt ist damit **platzierungsunabhängig**.
|
||
> ⇒ **Fund E** (`signal-range`-Boden am Manager) bleibt unverändert die richtige Maßnahme und ist nach
|
||
> dem Rückbau der **oberste offene Punkt** in TP25 — nicht erledigt, nicht gegenstandslos.
|
||
> 🔴🔴 **2026-08-22, Werkzeug-Fund mit Rückwirkung auf ALLE bisherigen Messungen: die
|
||
> `cap-wifiN`-Nummern sind NICHT stabil.** Der Manager vergibt sie dynamisch in der
|
||
> Reihenfolge, in der sich die CAPs anmelden — nach dem ap1-Rückbau auf `ether15` sind heute
|
||
> `cap-wifi1/2` = **ap2** und `cap-wifi3/4` = **ap1**, also genau umgekehrt zu der festen
|
||
> Tabelle, die in `tp6-marker/server.py` und `tp25-gehweg-auswertung.py` stand.
|
||
> ⚠️ **Daraus folgt NICHT, dass die alten Auswertungen falsch sind** — der erste Verdacht in
|
||
> diese Richtung war voreilig. Die Gegenprobe spricht dagegen: wendet man die *heutige* Karte
|
||
> auf die Raumkarte vom 08-21 an, wird **Technik OG von ap1 mit −43 dBm versorgt** — in dem
|
||
> Raum, in dem **ap2 steht**. Die alte Tabelle war zu ihrer Zeit also plausibel richtig und
|
||
> ist erst durch das Umstecken falsch geworden.
|
||
> ⇒ **Die Zuordnung gehört zum Messlauf, nicht zur Auswertungszeit.** `tp25-walk-messung.sh`
|
||
> friert sie jetzt als `ap-map.txt` im Lauf ein; die Auswertung nimmt ausschließlich diese
|
||
> Datei und lässt die Namen sonst roh (`cap-wifiN`) — unbeschriftet ist ehrlicher als falsch
|
||
> beschriftet. **Läufe vor dem 2026-08-22 sind nicht rekonstruierbar**; wer ihre AP-Namen
|
||
> zitiert, muss sie an der Physik prüfen (der AP im Raum liefert dort das stärkste Signal).
|
||
>
|
||
> 🆕 **Messaufbau neu gedacht (Christoph, 2026-08-22) — eine Runde statt zwei.** Das frei
|
||
> roamende iPhone zeigt ohnehin, welchen AP ein normaler Client wählt; interessant ist der
|
||
> **andere**. Das S22 wird deshalb per `access-list` automatisch auf den **Gegen-AP** des
|
||
> iPhones gezwungen (`docs/tp25-pin-s22.sh`, Automatik in der Klick-Konsole auf Port 3031).
|
||
> Pro Standort fallen so **zwei** Messpunkte an. Ein Doppelton meldet „beide angemeldet,
|
||
> verschiedene APs" = weitergehen.
|
||
> 🔴 **Die Falle dabei, live erlebt:** ein vom Pin **abgewiesener** Verbindungsversuch ist in
|
||
> den Ping-Daten **nicht** von einem Funkloch zu unterscheiden, und der Manager protokolliert
|
||
> ihn auf `info`-Ebene **gar nicht** (Beleg: zwischen `disconnected, access-list changed` und
|
||
> dem manuellen Reconnect stehen vier Minuten ohne eine einzige Zeile). Android quittiert die
|
||
> Abweisung mit „Verbindung fehlgeschlagen" und **wählt das Netz ab** — danach misst der Rest
|
||
> des Rundgangs Stille. Die Konsole schlägt deshalb mit Ton, Vibration und Vollbanner Alarm,
|
||
> sobald das gepinnte Gerät nirgends hängt, und schreibt nach 45 s vergeblicher Suche
|
||
> `UNERREICHBAR=apX` — denn „der Gegen-AP trägt hier nicht" **ist das Messergebnis**.
|
||
> ✅✅ **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** — gleiches Gerät, gleiche Rolle.
|
||
> Das Anheben von ap2 auf 2,5 m hat gewirkt; 1,0 s liegt unter der Hörbarkeitsschwelle.
|
||
> **Der Dead Spot, der TP25 seit dem 08-20 getragen hat, ist damit weg.**
|
||
> 🔎 **Neuer Kernbefund: das Netz ist gut, die Redundanz ist es nicht.** Die frei roamende
|
||
> Referenz hat in 17 von 21 Raumabschnitten **0 % Verlust**; der Gegen-AP dagegen 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 im Zustand „verbunden, aber tot".
|
||
> ⇒ **Dritter AP nach der eigenen Randbedingung derzeit NICHT begründet** — er kaufte Redundanz,
|
||
> keine Abdeckung. Das ist eine andere Frage und Christophs.
|
||
> ⇒ **Fund E weiter abgestuft:** der Sticky-Client vom 08-20 reproduzierte sich nicht (Treppe:
|
||
> sauberer Wechsel ap2→ap1, 0 % über 57 s). **Nicht umsetzen, bevor ein Sticky-Fall erneut
|
||
> gemessen ist.**
|
||
> ⚠️ **Korrektur zum 22:43-Lauf:** „Küche ist ein Loch für beide APs" gilt nur für die äußerste
|
||
> Ecke — im normalen Aufenthaltsbereich hat die Referenz dort **1 % Verlust bei −71 dBm**.
|
||
> ▶️ **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 (Ctrl-C) den Pin wieder. Auswertung danach mit `tp25-gehweg-auswertung.py --dir …`.
|
||
> ⏸️ **Stand bei Sitzungsende 2026-08-22: Rundgang noch nicht gegangen.** Pin gelöst,
|
||
> Automatik aus, `crs326-core` unverändert (die `access-list` steht unter keiner
|
||
> ComplianceRule, es gibt also nichts zu committen). Erster Messwert nebenbei: am Arbeitsplatz
|
||
> iPhone **ap2 −68**, S22 auf ap1 gezwungen **−77…−78**; ohne Pin geht das S22 sofort auf
|
||
> **ap2/5 GHz −71**.
|
||
> 📋 **Übergabe: [`handoff-tp25-session-20260821.md`](handoff-tp25-session-20260821.md).**
|
||
> ✅ **Transaktion `TEST-ether3-trunk-ap1-sideboard` am 2026-08-22 zurückgerollt und geschlossen**
|
||
> (Scope `bridge-vlan`) — `ether3` ist wieder untagged VLAN40, Compliance zurück auf 92/92.
|
||
> **Und das passiert TROTZ aktivem 802.11k/v und FT** — live geprüft: `st-main` mit `rrm=yes`/`wnm=yes`,
|
||
> `ft-over-ds=yes`. Die Merkregel `ref_ft_is_not_roaming` („11k/v fehlen") ist damit **historisch**.
|
||
> **Reihenfolge der Maßnahmen** (Doku §6, respektiert die User-Randbedingung „kein neuer AP, solange
|
||
> nicht alles aus den vorhandenen rausgeholt ist"): erst die Lücke messen → 5 GHz auf **20 MHz** statt
|
||
> 20/40 (+3 dB Link-Budget, für 1,2 Mbit Call irrelevant an Durchsatz) → **Fund E** (`signal-range`-Boden
|
||
> am Manager) → **2,4 GHz NICHT vorschnell drosseln** (dort hängen ~20 IoT-Geräte) → dritter AP zuletzt.
|
||
> **Restliste (Stand 2026-08-18):** Fund A–D ✅ · Drift-Luecke ✅ (mit TP28, 92/92) ·
|
||
> **offen: Fund E** (`access-list` mit `signal-range`-Boden — ⚠️ gehoert jetzt auf den **Manager**,
|
||
> nicht mehr auf die APs) · **Fund F** (5:05-Takt bei IoT-Trennungen, am Manager nachmessen —
|
||
> die AP-Tabellen sind unter CAPsMAN leer) · **Re-Walk + Dead-Spot-Kartierung** (hands-on).
|
||
> Alles drei ist Messarbeit gegen die korrigierte Basis, keine Konfigurationsarbeit mehr.
|
||
> **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), gehören also zusammen. **TP25 übernimmt den offenen Rest von TP3.3** (Re-Walk, Dead-Spot-Kartierung)
|
||
> — dieselbe physische Arbeit, doppelt geführt wäre Unsinn. TP3.2/TP3.3 bleiben als Historie stehen.
|
||
> **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.
|
||
|
||
- ⚠️ **Ausgangslage: Drift ist bestätigt, aber NICHT die Ursache.** Christoph hat am 08-16/17 selbst an den APs
|
||
gespielt („drift ist garantiert. es hat vorher aber auch nicht funktioniert"). Der Audit unten ist gegen den
|
||
**Ist-Stand** gefahren (`/interface/wifi/export` + `/interface/wifi/monitor` + Log, beide APs, 2026-08-17 14:01).
|
||
Zwei der Funde sind älter als die Experimente und stammen aus der TP3-Neuprovisionierung.
|
||
|
||
#### 🔴 Fund A — 2,4 GHz läuft auf 40 MHz, obwohl das Profil 20 MHz sagt (Beobachtung 4)
|
||
- **Christophs Frage war berechtigt, aber der Adressat ist ein Override, keine Entscheidung.** Das Channel-Profil
|
||
ist korrekt: `add band=2ghz-ax frequency=2412 name=ch-2g width=20mhz`. **Überschrieben wird es inline am Radio:**
|
||
`set [find default-name=wifi2] channel.band=2ghz-ax .skip-dfs-channels=10min-cac .width=20/40mhz`
|
||
→ **das Inline-Override gewinnt gegen das verlinkte Profil.** `monitor` beweist es: ap1 `2412/ax/Ce`,
|
||
ap2 `2462/ax/eC` — `Ce`/`eC` = 40 MHz. **Niemand hat 20/40 „gewählt"**; es ist genau die Override-Klasse, die
|
||
CLAUDE.md für den TP3-Bring-up schon dokumentiert („Werks-Inline-Overrides auf wifi1/wifi2 überschreiben das
|
||
verlinkte Profil") — damals nur für `configuration.ssid`/`security.*` bereinigt, für `channel.*` **nie**.
|
||
- **Warum 40 MHz auf 2,4 GHz falsch ist (Manual-Ebene, nicht Geschmack):** es gibt nur 3 überlappungsfreie
|
||
20-MHz-Kanäle. ap1 belegt mit `2412/Ce` ≈ 2402–2442, ap2 mit `2462/eC` ≈ 2442–2482 → **die beiden APs decken
|
||
zusammen praktisch das ganze Band ab.** Jeder Nachbar-AP überlappt zwangsläufig einen von beiden. Dazu halbiert
|
||
40 MHz die spektrale Leistungsdichte → **~3 dB weniger Reichweite bei gleicher Sendeleistung.** Und der Nutzen
|
||
ist null: IoT-Clients sind fast alle 20-MHz-only. **Das ist die „miserable Reichweite" aus Beobachtung 4.**
|
||
- **Fix:** die Inline-`channel.*`-Overrides auf `wifi1`/`wifi2` **leeren** (`!channel`), damit die Profile greifen.
|
||
Nebenbefund: `skip-dfs-channels=10min-cac` auf `wifi2` ist bedeutungslos — im 2,4-GHz-Band gibt es keine
|
||
DFS-Kanäle. Copy-Paste-Spur, harmlos, gehört trotzdem raus.
|
||
|
||
#### 🔴 Fund B — ap2 sendet auf 5 GHz mit 9 dBm statt 18 dBm (Beobachtungen 1+2)
|
||
- **Gemessen:** ap1 `wifi1` `tx-power: 18`, ap2 `wifi1` **`tx-power: 9`** — bei identischer Hardware und
|
||
identischem Profil. **9 dB Unterschied = Faktor ~8 in der Leistung**, ap2s 5-GHz-Zelle hat grob ein Drittel
|
||
des Radius von ap1.
|
||
- ✅ **BEHOBEN 2026-08-17: ap2s 5-GHz-Radio läuft jetzt mit 18 dBm** (`5220/ax/Ce/I`). Messreihe am Gerät:
|
||
| Zustand | Kanal | tx-power |
|
||
|---|---|---|
|
||
| ap2 vorher | `5745/ax/Ceee` (80 MHz, ohne `/I`) | **9** |
|
||
| ap2 nach Frequenzwechsel | `5220/ax/eeCe/I` (80 MHz, mit `/I`) | **9** ⬅️ unverändert! |
|
||
| ap2 nach Breitenkorrektur | `5220/ax/Ce/I` (40 MHz) | **18** ✅ |
|
||
| ap1 durchgehend | `5180/ax/Ceee/I` → `5180/ax/Ce/I` | 18 → 18 |
|
||
- 🔴 **Ehrliche Korrektur: meine erste Begründung war falsch, und die zweite auch nicht vollständig.**
|
||
Ich hatte die 9 dBm auf **ETSI-Band C** (ch149 ohne `/I`-Flag) zurückgeführt. **Widerlegt:** der Wechsel ins
|
||
Indoor-Band bei gleicher Breite ließ die Leistung auf 9 dBm. „80 MHz kostet generell Leistung" trägt auch
|
||
nicht — **ap1 fuhr 80 MHz im selben Band mit 18 dBm.** Der Fix ist gemessen, der Mechanismus ist es nicht.
|
||
- 🔬 **Kohärente Resthypothese (nicht bewiesen, aber testbar):** es sind **zwei** Effekte, je einer pro Kanal —
|
||
(a) `5745/Ceee` = ETSI-Band-C-Limit (die erste Theorie gilt für *diesen* Kanal weiter), und
|
||
(b) `5220/eeCe` = das Kontrollkanal liegt **nicht** am Blockanfang (`eeCe` statt `Ceee`), was das Radio
|
||
derated. Das würde ap1s `5180/Ceee`=18 und ap2s `5220/eeCe`=9 gleichzeitig erklären.
|
||
**Diskriminierender Test:** ap2 testweise auf `5180/Ceee` — 18 dBm ⇒ Hypothese (b) trägt; 9 dBm ⇒ es ist
|
||
gerätespezifisch (dann ist ap2s Radio der Verdächtige, relevant für die Hardware-Frage!). **Nicht nötig für
|
||
den Betrieb, aber vor einem Hardwarekauf wissenswert.**
|
||
- ⚠️ **Die TP3-Notiz „Kanäle non-DFS ap1 ch36/ch1, ap2 ch149/ch11" hatte trotzdem keinen tragenden Grund** —
|
||
s. eigener Absatz „Herkunft der ch149-Notiz" unten.
|
||
- 🎯 **Der Fix löst Fund B und C in einem Zug — und er ist nicht offensichtlich:** unter ETSI Austria existiert
|
||
im Indoor-Band (5180–5240) **nur ein einziger 80-MHz-Block** (`5180/Ceee` = ch36–48). Zwei APs können dort
|
||
also nicht beide 80 MHz fahren, ohne sich zu überlappen — **genau deshalb ist ap2 überhaupt auf 5745 gelandet.**
|
||
Mit **40 MHz** gehen beide sauber ins Indoor-Band:
|
||
| AP | neu | Kanäle | Leistung | Belegung |
|
||
|---|---|---|---|---|
|
||
| ap1 | `5180/Ce` | 36+40 | **18 dBm** | 5170–5210 |
|
||
| ap2 | `5220/Ce` | 44+48 | **18 dBm** | 5210–5250 |
|
||
Beide `/I`-freigegeben (aus der Radio-Tabelle bestätigt), überlappungsfrei, **+9 dB für ap2**.
|
||
|
||
#### 🔴 Fund C — 80 MHz auf 5 GHz kostet Reichweite, die hier fehlt (Beobachtung 1)
|
||
- `ch-5g width=20/40/80mhz`, laufend `5180/ax/Ceee` bzw. `5745/ax/Ceee` = **80 MHz**. Dieselbe Sendeleistung auf
|
||
die vierfache Bandbreite verteilt kostet **~6 dB** effektiven SNR am Zellenrand gegenüber 20 MHz, ~3 dB
|
||
gegenüber 40 MHz. In einer 2-AP-Wohnung mit Terrasse ist Spitzendurchsatz das falsche Optimierungsziel —
|
||
**Randabdeckung und Stabilität sind das richtige.**
|
||
- **Beleg, dass genau das das Terrassen-Symptom ist:** ap1-Log, Telefon steht still im Halter:
|
||
`-81` → roamt auf 2,4 GHz `-82` → zurück `-83` → **`disconnected, connection lost, signal strength -88`**.
|
||
**−88 dBm ist unter der Nutzbarkeitsschwelle.** Das ist **kein** Roaming-Problem (Christoph sagt selbst
|
||
„ich red da nicht mal von roaming"), sondern **Link-Budget** — und dagegen hilft Breite runter, nicht drehen.
|
||
|
||
#### 🔴 Fund D — `w2-iot` trägt einen unzulässigen Channel-Override auf einem Slave-VAP (Beobachtung 3)
|
||
- **ap1:** `add channel.band=2ghz-n .width=20mhz configuration=cfg-iot … master-interface=wifi2 name=w2-iot`
|
||
- **ap2:** `add channel.width=20mhz configuration=cfg-iot … master-interface=wifi2 name=w2-iot`
|
||
- **Ein Slave-VAP teilt das Radio des Masters und kann keinen eigenen Kanal haben.** Auf ap1 erzwingt das
|
||
Override zusätzlich `2ghz-n`, **während der Master `wifi2` auf `2ghz-ax` läuft** — ein Band-Mismatch zwischen
|
||
Master und Slave ist keine unterstützte Konfiguration. **Das ist der Mechanismus hinter „alle SSIDs sind da,
|
||
`-iot` beharrlich nicht, und plötzlich ist es wieder da":** der VAP fällt aus der Beacon-Rotation und kommt
|
||
bei einem Re-Apply zurück.
|
||
- 📌 **Und es ist der Beweis für Beobachtung 6:** die beiden APs sind hier **nicht identisch** (ap1 mit Band+Width,
|
||
ap2 nur Width). Eine generierte Config kann das nicht produzieren — das ist Handarbeit, und zwar auf genau dem
|
||
Objekt, das keine Handarbeit verträgt.
|
||
|
||
#### 🟠 Fund E — keine `access-list`, also kein Signal-Boden gegen klebende Clients (Beobachtung 2)
|
||
- **`/interface/wifi/access-list` ist auf beiden APs leer.** Damit gibt es **keinen `signal-range`-Boden** — ein
|
||
Client darf hängen bleiben, bis die Verbindung zusammenbricht, statt frühzeitig abgestoßen zu werden. Das ist
|
||
der Standard-RouterOS-Griff gegen genau dieses Symptom.
|
||
- **Beleg für das Kleben:** ap2-Log, Client `56:71:6C:22:E6:A9` flappt in 22 Minuten **sechsmal** zwischen
|
||
`wifi1`↔`wifi2` bei −62…−83 dBm. Und: `F2:62:4C:E1:AF:09@wifi2 disconnected, not responding, signal strength -47`
|
||
— **Abbruch bei ausgezeichneten −47 dBm.** Beides zusammen = Band-Oszillation ohne Steering-Politik, nicht
|
||
schlechte Abdeckung.
|
||
- ⚠️ **Nebenbefund:** die TP3.2-Access-List, die den P750W an ap1 pinnte („überlebt ap2-Re-Provision"), ist
|
||
**beim Rebuild 2026-08-08 verlorengegangen** — im Export beider APs steht keine einzige Access-List-Zeile.
|
||
Laut CLAUDE.md ist der Pin nicht mehr nötig (L3-GW liegt auf dem Core), der **Verlust war aber unbemerkt.**
|
||
- `ft=yes ft-over-ds=no` war **korrekt für den damaligen Standalone-Betrieb** (over-DS braucht ein
|
||
Inter-AP-Protokoll) — kein Fund, nur zur Abgrenzung festgehalten.
|
||
📌 **Seit TP28 gilt das nicht mehr:** unter CAPsMAN ist **`ft-over-ds=yes`** aktiv, weil der Manager
|
||
genau dieses Inter-AP-Protokoll mitbringt.
|
||
|
||
#### 🟡 Fund F — IoT-Clients trennen exakt alle ~5:05 bei gutem Signal (offen, präzise vermessen)
|
||
```
|
||
12:55:29 connected -59 → 13:00:36 disconnected -59 (5:07)
|
||
13:17:27 connected -63 → 13:22:32 disconnected -63 (5:05)
|
||
13:35:32 connected -60 → 13:40:37 disconnected -59 (5:05)
|
||
13:57:31 connected -59 → 14:02:35 disconnected -58 (5:04)
|
||
```
|
||
- **−59 dBm ist ein gutes Signal. Eine auf 3 Sekunden reproduzierbare 5-Minuten-Periode ist ein Timer, kein Funk.**
|
||
- 🚫 **Ausdrücklich NICHT die alte Power-Save-Merkregel wiederbeleben** — die ist am 2026-08-13 als Fehldiagnose
|
||
zurückgezogen worden (`ref_wifi_powersave_multicast`, Drucker war schlicht aus). Dieser Befund ist ein *anderer*:
|
||
vier saubere Messpaare mit konstanter Periode.
|
||
- **Kandidaten + der Test, der sie trennt:** (a) periodischer VAP-Re-Apply durch Fund D → prüfen, ob ap2
|
||
(ohne Band-Override) dieselbe Periode zeigt; (b) DHCP-Lease/Renew-Interaktion → `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 nach Fund-D-Fix unverändert. **Erst nach dem Fund-D-Fix erneut messen** — sonst misst man zwei
|
||
Effekte übereinander.
|
||
|
||
#### 🧭 Beobachtung 5 — „alle Welt sagt CAPsMAN, nur wir sagen Nautobot GC. Hilfe oder Last?"
|
||
- **Klare Antwort: es sind keine Alternativen, sie lösen verschiedene Probleme.** CAPsMAN = *Runtime*-Control-Plane
|
||
fürs WLAN (ein Ort für Radio-Settings, sofortiger Push, dynamische VAPs). Nautobot GC = *Config-as-Code*-SoT
|
||
+ Drift-Erkennung über die **ganze** Flotte (Core, Branch, beide APs). Man kann beides haben; das Reibungspunkt
|
||
ist nur, dass CAPsMANs dynamische Objekte für GC unsichtbar sind (der Grund von 2026-07-22, und der gilt).
|
||
- 🔎 **Aber eine Begründung von damals hält der Prüfung nicht stand.** Der Entscheid nannte als ersten Grund
|
||
„WLAN dauerhaft instabil, besonders wenn beide APs laufen". **Das WLAN ist nach dem Standalone-Rebuild
|
||
weiterhin instabil** — die These „CAPsMAN war die Ursache" ist damit empirisch geschwächt. Die Funde A–E sind
|
||
Funkparameter; CAPsMAN hätte sie weder verursacht noch geheilt. **Der Rebuild hat ein Symptom umgezogen,
|
||
keine Ursache beseitigt.**
|
||
- **Wo GC hier tatsächlich Last war:** es hat diesen Drift **nicht verhindert.** Die Compliance deckt
|
||
`wifi-steering` ab (seit TP3.3), aber offensichtlich **nicht die Channel-/Inline-Override-Fläche** — sonst wären
|
||
Fund A und D als Drift aufgeschlagen. Das ist eine **Lücke im Feature-Set, kein Argument gegen GC.**
|
||
- **Ehrliches Urteil (meine Empfehlung):** GC als SoT **behalten** — es ist das Einzige, was Hand-Tuning davon
|
||
abhält, sich still zu akkumulieren, und genau das ist hier passiert. Aber: **GC ist kein WLAN-Tuning-Werkzeug.**
|
||
Der Ausweg ist nicht CAPsMAN, sondern die Parameter **einmal nach Manual richtig** zu setzen und dann mit einer
|
||
Compliance-Feature auf `/interface/wifi` **festzunageln**. Bei genau 2 APs ist der Kanal-/Leistungsplan eine
|
||
Einmalentscheidung, kein Laufzeitproblem — der Hauptvorteil von CAPsMAN greift hier also kaum.
|
||
*(Wenn ein dritter AP dazukäme, wäre die Abwägung neu zu führen.)*
|
||
|
||
#### 🔎 Herkunft der ch149-Notiz (Christophs Frage 2026-08-17: „wie kam es zu dieser Notiz?")
|
||
- **Nachverfolgt bis Commit `03f328f`, 2026-06-18, CAPsMAN-Ära.** Wortlaut der Commit-Message:
|
||
*„Kanal-Plan non-DFS/überlappungsfrei: ap1 ch36+ch1, ap2 ch149+ch11 (Auto-Channel hatte beide APs auf
|
||
5500-DFS/2437 gelegt = Co-Channel)"*.
|
||
- 🎯 **Das gelöste Problem war Co-Channel, nicht DFS.** Auto-Channel hatte beide APs auf **denselben** Kanal
|
||
gelegt; der Fix war, überhaupt zu pinnen. Dass die Wahl auf 36/149 fiel, ist das **FCC-Standardpaar** aus
|
||
jedem Tutorial — „non-DFS" wurde als Begründung *nachträglich* mitgeschrieben, war aber nie eine geprüfte
|
||
Anforderung. **Christophs Einschätzung ist damit belegt: es gibt keinen Grund, Frequenzbänder auszunehmen.**
|
||
- 📌 **Und ein Detail, das die Sache entschärft: `skip-dfs-channels=10min-cac` schließt DFS gar nicht aus.**
|
||
Es überspringt nur Kanäle, die die **10-Minuten**-CAC brauchen (Wetterradar-Band 5600–5650). Der Rest des
|
||
DFS-Bands bliebe nutzbar. Ausgeschlossen wurde DFS in Wahrheit durch das **harte `frequency=`-Pinning** —
|
||
wo nichts ausgewählt wird, wirkt kein Skip. (Die Overrides sind mit Schritt 2 ohnehin weg.)
|
||
- **Konsequenz für den Kanalplan:** das DFS-Band **5500–5700** ist eine echte Option und liegt bisher brach —
|
||
**11 zusätzliche Kanäle**, in ETSI ohne `/I`-Beschränkung. Preis: CAC-Wartezeit beim Start und im
|
||
Radar-Fall ein erzwungener Kanalwechsel (30 min Sperre). Bei Christophs aktuellem Symptombild
|
||
(Abbrüche) wäre ein radar-getriggerter Wechsel unangenehm — **deshalb jetzt nicht**, aber es ist der
|
||
Reservekanalraum, falls ein dritter AP dazukommt und 40 MHz nicht mehr reicht.
|
||
|
||
#### 📡 Beobachtung 4b — „könnte `2ghz-g` nicht besser zu IoT passen?"
|
||
- **Verfügbar auf diesem Radio** (`/interface/wifi/radio`): `2ghz-g:20mhz` · `2ghz-n:20mhz,20/40mhz` ·
|
||
`2ghz-ax:20mhz,20/40mhz`.
|
||
- ❌ **Für Reichweite bringt es nichts.** `band=` setzt die **höchste** angebotene Generation, nicht die
|
||
einzige — ein `2ghz-ax`-AP ist rückwärtskompatibel, g/n-Clients assoziieren normal. Und ein schwacher
|
||
Client fällt über die Ratenadaption von selbst auf die robusten niedrigen Raten. `band` ändert weder
|
||
Sendeleistung noch Empfängerempfindlichkeit.
|
||
- ✅ **Für Interoperabilität ist der Gedanke aber richtig.** Billige ESP-/Realtek-IoT-Stacks haben
|
||
dokumentierte Probleme mit 802.11ax-Beacons (HE-Capabilities, PMF). `2ghz-n` nimmt die HE-Elemente aus
|
||
dem Beacon und kann Assoziationswackler beheben — ein **Kompatibilitäts-**, kein Abdeckungsargument.
|
||
- 🎯 **Pointe: genau dieser Instinkt steckte schon im Fehler.** `channel.band=2ghz-n` auf `w2-iot` (Fund D)
|
||
war exakt der Versuch, IoT ein legacy-freundlicheres Band zu geben. **Die Absicht war richtig, der
|
||
Mechanismus falsch** — `band` ist eine Eigenschaft des **Radios**, nicht eines Slave-VAPs. Alle
|
||
2,4-GHz-SSIDs teilen `wifi2`.
|
||
- **Empfehlung, falls Fund F (5-Minuten-Takt) nach dem Override-Fix bleibt:** `wifi2` global auf **`2ghz-n`**
|
||
— die saubere Version dessen, was der Override wollte. Kostet real nichts (kein 2,4-GHz-Client hier braucht
|
||
ax-Raten, `dirtyvader-main` auf 2,4 GHz ist ohnehin Fallback). **`2ghz-g` NICHT als ersten Schritt:** die
|
||
20-MHz-Bindung ist richtig, aber 54 Mbit/s-Deckel und kein HT für *alle* 2,4-GHz-Clients ist ein echter
|
||
Verlust ohne Zusatznutzen gegenüber `n`. `g` ist die Eskalationsstufe, nicht der Einstieg.
|
||
|
||
#### 🏠 Deckenmontage-APs — bringen sie einen echten Vorteil? (Christophs Überlegung 2026-08-17)
|
||
- **Der Gewinn liegt in Platzierung und Antennendiagramm, nicht im Silizium.** Ein cAP ax ist dieselbe
|
||
Chip-Generation und ebenfalls 2×2 wie der hAP ax² — **mehr Rohleistung gibt es nicht.** Was er gibt:
|
||
ein nach unten optimiertes Diagramm, freie Sichtlinien von der Decke, PoE-Montage ohne Möbel davor.
|
||
- 🔴 **Aber die Entscheidung wäre heute blind gewesen.** Vor dieser Session lagen ~9 dB auf ap2 brach
|
||
(Fund B), 2,4 GHz verschenkte ~3 dB an 40 MHz (Fund A) und der 5-GHz-Zellenrand ~3 dB an 80 MHz (Fund C).
|
||
**Hardware zu kaufen, um Fehlkonfiguration zu kompensieren, ist der teuerste Weg.** Genau das deckt sich
|
||
mit Christophs eigener Randbedingung („nicht kaufen, bevor alles aus dem AP raus ist").
|
||
- ✅ **Jetzt ist der Boden gelegt** (beide APs 18 dBm, überlappungsfrei, 20 MHz auf 2,4). **Die Entscheidung
|
||
gehört an den Re-Walk (Schritt 5).** Bleiben danach Dead Spots — Terrasse, Kanten —, dann *ist* es ein
|
||
Platzierungsproblem, und Deckenmontage ist die richtige Antwort. Dann stellt sich aber zuerst die
|
||
billigere Frage: **reicht Umhängen der vorhandenen APs?** „Drehen hat nicht geholfen" passt zu einer
|
||
Abschattung, die Drehen nicht behebt — Versetzen schon.
|
||
- 📌 **Falls es auf drei APs hinausläuft, wird die CAPsMAN-Abwägung neu geführt** (s. Beobachtung 5): bei
|
||
3 APs ist koordinierte Kanal-/Leistungsverwaltung erstmals ein Laufzeit- und nicht mehr ein
|
||
Einmalproblem. Und der diskriminierende Test aus Fund B (ist ap2s Radio gerätespezifisch schwach?)
|
||
gehört **vor** einen Kauf.
|
||
|
||
#### 📋 Umsetzungsreihenfolge (alles via GC, gestaffelt, ap2 zuerst)
|
||
1. ✅ **Fund D DURCH (2026-08-17)** — `channel.*`-Overrides von `w2-iot` auf **beiden** APs entfernt.
|
||
`w2-iot` ist danach auf ap1 **und** ap2 `BR` (bound + running) → Beobachtung 3 strukturell erledigt.
|
||
Dateien: `routeros/tp25-schritt2-fix-overrides-ap2.rsc`, `routeros/tp25-schritt1-3-ap1.rsc`.
|
||
2. ✅ **Fund A DURCH (2026-08-17)** — Inline-`channel.*` auf `wifi1`/`wifi2` geleert. **2,4 GHz läuft jetzt
|
||
auf 20 MHz** (`2412/ax` bzw. `2462/ax`, kein `Ce`/`eC` mehr), tx-power 15 dBm. ap1 ch1 / ap2 ch11 =
|
||
überlappungsfrei mit Platz für ch6 in der Nachbarschaft.
|
||
3. ✅ **Fund B+C DURCH (2026-08-17)** — `ch-5g` auf `width=20/40mhz`; ap1 `5180/ax/Ce/I`, ap2 `5220/ax/Ce/I`,
|
||
**beide 18 dBm** (ap2 vorher 9). Zellen benachbart, überlappungsfrei (5170–5210 / 5210–5250).
|
||
- 🔴 **GOTCHA, der zwei Deploys gekostet hat: `/interface/wifi/set <if> !channel` löscht die
|
||
Inline-Sub-Property-Overrides NICHT** — es wirft nicht, es tut nichts. Danach stand `channel.width`
|
||
unverändert da, obwohl Profil *und* `configuration` korrekt auflösten. **Richtig ist einzeln:
|
||
`!channel.width !channel.band !channel.skip-dfs-channels`.** Dasselbe Muster wie der bekannte
|
||
`!configuration.ssid !security.*`-Griff aus dem TP3-Bring-up — nur nie auf `channel.*` angewandt.
|
||
- ⚠️ **Zwischenzustand war schlechter als der Ausgangszustand:** weil die Breite noch auf 80 MHz stand,
|
||
rutschte ap2 mit der neuen Frequenz in **denselben** 80-MHz-Block wie ap1 (beide ch36–48) = genau die
|
||
Co-Channel-Lage, die 2026-06-18 wegkonfiguriert werden sollte. **Merke: bei Kanalarbeit Breite VOR
|
||
Frequenz ändern**, sonst erzeugt man auf dem Weg eine Kollision.
|
||
4. 🔜 **Fund E** — `access-list` mit `signal-range`-Boden je AP; Schwellen erst nach dem Re-Walk festlegen.
|
||
*(Hinweis aus der P750W-Historie: `/interface/wifi` hat **kein** `disconnect-timeout`/`keepalive` mehr —
|
||
das gab es nur im alten `wireless`-Paket, AX-Radios können nicht zurück. Der Signal-Boden ist damit der
|
||
einzige verbleibende Griff gegen klebende Clients.)*
|
||
5. **Re-Walk + Dead-Spot-Kartierung** (der übernommene TP3.3-Rest) — **erst jetzt**, gegen die korrigierte Basis.
|
||
Vorher gemessene Werte sind wertlos.
|
||
6. ⏳ **Drift-Lücke — Teil 1 GESCHLOSSEN, Teil 2 offen und jetzt exakt vermessen (2026-08-17).**
|
||
- ✅ **Der akute Teil ist zu:** die neuen Werte stehen in der SoT (`nautobot/ingest-ap-configcontext.py`:
|
||
`radios[5g].width` `20/40/80mhz`→`20/40mhz`, `DEVICE_CHANNELS[ap2][5g]` `149`→`44`), Config-Context
|
||
gepatcht, dann **Backup → Intended → Compliance** → **72/72, keine Drifts.** Damit kann ein künftiger
|
||
Deploy die Fixes **nicht** mehr zurückdrehen. *(Bemerkenswert: `width` für 2,4 GHz stand dort die ganze
|
||
Zeit korrekt auf `20mhz` — die SoT war richtig, nur das Gerät hat sie nicht befolgt. Der Beweis, dass
|
||
das Problem der Inline-Override war und nicht die Datenlage.)*
|
||
- ✅ **ERLEDIGT 2026-08-18 (mit dem TP28-GC-Umbau).** Die `wifi-*`-Features haengen jetzt am Core,
|
||
die APs sind auf die `cap`-Zeile eingedampft, und drei neue Rules (`wifi-capsman`,
|
||
`wifi-provisioning`, `wifi-cap`) decken den Rest ab. **92/92 compliant.** Die unten
|
||
beschriebene Luecke auf `/interface wifi` ist damit gegenstandslos: ein CAP hat keine
|
||
Inline-Overrides mehr. Der historische Befund bleibt als Begruendung stehen:
|
||
- 🔴 **Der Erkennungsteil bleibt offen. Die 18 Compliance-Rules abgefragt — es fehlen genau zwei Sektionen:**
|
||
| vorhanden | fehlt |
|
||
|---|---|
|
||
| `/interface wifi channel` · `… configuration` · `… datapath` · `… security` · `… steering` | **`/interface wifi`** (die Interface-Sektion) |
|
||
| `/ip firewall nat` | **`/ip firewall filter`** |
|
||
- **Folge 1 (WLAN):** die fünf `wifi-*`-Features decken nur die **Profil**objekte ab. Die Inline-Overrides
|
||
leben in `/interface wifi` — **deshalb sind Fund A und D seit dem 2026-08-08 unentdeckt geblieben, obwohl
|
||
die Flotte durchgehend „compliant" meldete.** Genau die Lücke, die ich unter Beobachtung 5 vermutet hatte;
|
||
sie ist jetzt belegt, nicht vermutet. Erschwerend: die VAP-MAC-Adressen sind gerätespezifisch (Template
|
||
Zeile ~220 nimmt sie bewusst raus), eine Rule auf diese Sektion braucht also erst
|
||
`local_config_context_data` je AP.
|
||
- 📋 **Vorarbeit `/ip firewall filter` (2026-08-17, Reconnaissance — vor dem Template-Bau nötig):**
|
||
`crs326-core` **25** Regeln (defconf + TP10 + 6 TP5-Holes + 11 TP5-E2 + Log-Regel) · `crs326-branch` **0** ·
|
||
`ap1`/`ap2` je **12 defconf-Regeln** (Werksreste: input-seal, fasttrack, ipsec-Zeilen, sogar
|
||
„accept to local loopback (for CAPsMAN)" — inert, weil die APs nicht routen, aber vorhanden).
|
||
➡️ **Zwei Entscheidungen, bevor die Rule scharf gemacht wird**, sonst meldet sie sofort flächendeckend Drift:
|
||
(a) **APs:** die 12 defconf-Regeln als Sollzustand ins Template schreiben **oder** sie abräumen
|
||
(sie tun nichts — ein reiner AP hat keine `forward`-Kette zu schützen). Empfehlung: **abräumen**, sonst
|
||
schreibt man Werksmüll als Absicht in die SoT — dasselbe Argument wie bei `telnet` in TP16.1.
|
||
(b) **branch:** 0 Regeln ist der korrekte Sollzustand und muss im Template auch als *leer* gerendert werden.
|
||
- ✅ **Folge 2 — GESCHLOSSEN (2026-08-17, TP26): `/ip firewall filter` hat jetzt eine Compliance-Rule.**
|
||
Vorher gab es nur `ip-firewall-nat`; „72/72" sagte über die Firewall gar nichts. Jetzt **76/76**
|
||
(eine neue Rule × 4 Geräte). `config_ordered=true` — bei Firewallregeln ist die Reihenfolge semantisch.
|
||
**Damit ist TP5 Etappe 3 entblockt.**
|
||
- ⚠️ **`steering=`/Channel-Änderungen re-applien alle VAPs und werfen jeden Client kurz raus** (TP3.3 am
|
||
2026-08-13 gemessen: ap1 25→2, ap2 7→14). **Nicht während eines Calls deployen.** `/system backup` je AP vorher.
|
||
- **Abh.:** TP22 ✅ (GC-Deploy-Pipeline). Übernimmt den offenen Rest von **TP3.3**, verwandt **TP3.2** (Coverage-Doku
|
||
`docs/wifi-p750w-coverage-and-map.md`). **Kein Blocker für TP5/TP6** — läuft parallel.
|
||
|
||
<a id="tp39"></a>
|
||
|
||
### TP39 — LXC-Hygiene auf pve: Resolver-Altlasten, `networking.service`, DB-Reverse-Lookup ▸ ✅ **ABGESCHLOSSEN (2026-09-01)**
|
||
|
||
**Auslöser:** ein 10-Sekunden-Hänger auf `gitea`, gefunden am Rand des TP16.3-§5-Flips. Er hatte mit
|
||
dem Flip nichts zu tun, war aber nur deshalb sichtbar, weil zum ersten Mal jemand systematisch
|
||
gemessen hat. Beim Beheben kam eine zweite, unabhängige Altlast zum Vorschein. Beide sind
|
||
**Hygiene an denselben pve-LXCs** und gehören in **ein** Wartungsfenster — deshalb ein TP, nicht zwei.
|
||
|
||
#### Was am 2026-09-01 bereits erledigt ist (Ausgangslage, nicht Aufgabe)
|
||
|
||
- **Ursache des Hängers:** MariaDB (LXC **302**, `10.40.20.23`) hatte `skip-name-resolve`
|
||
**auskommentiert** ⇒ Reverse-Lookup bei **jeder neuen Verbindung**; `/etc/resolv.conf` zeigte auf
|
||
den **toten** `10.40.41.254` (VLAN1-Altschema). **Gemessen: 10.010 ms je Lookup.**
|
||
Pool-Verbindungen waren schnell ⇒ das Symptom wirkte sporadisch.
|
||
- **Blast-Radius, per Verbindungstabelle belegt:** an dieser DB hängen **Vaultwarden (`.127`),
|
||
Home Assistant (`.105`) und gitea (`.157`)**.
|
||
- **Fix ausgerollt** (`pct set <id> --nameserver 10.40.20.1` + Reboot) für **fünf** LXCs:
|
||
`302 mariadb`, `102 mqtt5`, `106 influxdb2`, `107 redis`, `150 iob-exposed`.
|
||
**Nachher: Lookup 21–24 ms, gitea 0 von 25 Proben langsam** (vorher ~10 % bei 10,1 s).
|
||
|
||
#### Scope (alle Punkte erledigt)
|
||
|
||
1. ✅ **ERLEDIGT 2026-09-01: `skip-name-resolve` aktiviert.** Vorher `mysql.user` gelesen (User gab
|
||
den DB-Zugang per `/root/.my.cnf`) — **kein einziger Grant lautet auf einen DNS-Hostnamen**, alle
|
||
sind `%` / IP-Literal / `localhost`. Damit kann `skip-name-resolve` niemanden aussperren; der
|
||
Reverse-Lookup war reiner Ballast. Gesetzt als eigenes Drop-in
|
||
`/etc/mysql/mariadb.conf.d/90-skip-name-resolve.cnf` (nicht die `#`-Zeile in `50-server.cnf`
|
||
editiert — Paket-Update-stabil), `@@skip_name_resolve=1` nach Neustart verifiziert.
|
||
**Wirkung: gitea/vw/hass je 0 von 12 frischen Anfragen langsam, max 0,25 s.** Alle drei Clients
|
||
danach wieder verbunden. Die Ursache ist damit **beseitigt**, nicht nur entschärft.
|
||
🔴 **Sicherheitsvorfall dabei:** die `.my.cnf` trug `password <wert>` **ohne `=`**, die Maskierung
|
||
griff nicht ⇒ das **root-DB-Passwort ist ins Transkript geraten** und gehört rotiert (→ TP30).
|
||
2. ✅ **ERLEDIGT 2026-09-01: `networking.service` + `ifupdown-wait-online.service` in `101`, `102`,
|
||
`106`, `107` repariert.** **Ursache — dieselbe Altlast wie beim Resolver:** in
|
||
`/etc/network/interfaces` standen noch `auto`-Stanzas aus dem **alten VLAN1-Schema**
|
||
(`eth0` → `10.40.41.x`) und aus früheren Versuchen (`veth16`, `veth24`, ein Tippfehler `eth01`) —
|
||
**alle auf Interfaces, die es im Container nicht gibt.** `ifup -a` scheiterte daran, während das
|
||
echte `veth20` sauber hochkam: „failed" bei funktionierendem Netz.
|
||
**Fix:** nur die toten Stanzas entfernt (Backup je Container:
|
||
`/etc/network/interfaces.bak-20260901`), `lo`/`veth20`/`veth20.2` unangetastet.
|
||
**Beweis ist ein echter Neustart, nicht der Servicestatus:** `106` neu gestartet ⇒ **influxdb kam
|
||
nach 5 s von selbst hoch**, `networking active`, **null** fehlgeschlagene Units. Genau der Fall,
|
||
der vorher scheiterte.
|
||
🔑 **Nicht durch die Session verursacht:** `101` wurde vor dem Fix nie angefasst und zeigte dasselbe
|
||
Bild — die Container liefen nur 13+ Tage ohne Neustart, deshalb war es unsichtbar.
|
||
🟡 **Rest, bewusst liegen gelassen:** `run-rpc_pipefs.mount` (101, 150) und
|
||
`proxmox-regenerate-snakeoil.service` (108) — vorbestehend, ohne Bezug zu Netz oder DNS.
|
||
3. ✅ **ERLEDIGT 2026-09-01 (im selben Fenster aufgetaucht): LXC 302 war zu 100 % voll** (62/65 GB),
|
||
Rootfs `+30G` online gewachsen ⇒ 95 GB / 69 %, MariaDB lief durch. 🔴 **Das ist ein Puffer, kein
|
||
Fix:** 59 GB davon sind die `homeassistant`-DB (`states.ibd` allein **41 GB**), die unbegrenzt
|
||
wächst (~1 GB/Tag) — die dauerhafte Lösung ist HA-seitig und liegt in **TP40**.
|
||
4. ✅ **ERLEDIGT 2026-09-01: Resolver-Sweep durchgezogen — pve UND NAS.** **pve:** alle 13 LXCs
|
||
(`101,102,105,106,107,108,110,111,112,130,150,302,400`) tragen `nameserver 10.40.20.1`, **kein
|
||
`.41.254` mehr** — nicht nur die fünf geänderten. **NAS (DS923) via Portainer-API ep14:** alle
|
||
20 Container gelistet, keiner mit `.41.254`-DNS-Override; alle laufenden lösen über den
|
||
Embedded-Resolver (`127.0.0.11`) in **66–110 ms** auf (rc=0), inkl. Grenzfall `gitea` (erstellt
|
||
2026-07-10, um den Host-Fix herum) — **kein eingefrorener Dead-Resolver**, kein Recreate nötig.
|
||
NAS-Host `/etc/resolv.conf` = `10.40.20.1`. Als **wiederkehrende Kontrolle** bleibt der Sweep als
|
||
CLAUDE.md-Gotcha verankert (pve-Einzeiler + Synology-DSM-Fallstrick von 2026-07-10).
|
||
5. ✅ **ERLEDIGT 2026-09-01: Abnahme-Regel festgeschrieben.** „Vor jedem geplanten LXC-Neustart
|
||
`systemctl --failed` lesen und danach die **Ports gegen vorher** vergleichen" steht als
|
||
CLAUDE.md-Gotcha (Fall `influxdb2` wäre sonst still durchgerutscht).
|
||
|
||
#### Abhängigkeiten
|
||
|
||
- Keine blockierenden. Punkt 1 brauchte **DB-Credentials** (siehe **TP30**, Credential-Bestand).
|
||
- Punkt 2 war unabhängig und lief zuerst.
|
||
|
||
#### Abgrenzung
|
||
|
||
- **Nicht** TP16.3: dort geht es um cloudflared/Tunnel: der DNS-Fund kam nur *dabei* ans Licht.
|
||
- **Nicht** TP14.2a: das war 2026-07-10 derselbe Fehlertyp an **anderen** Hosts (NAS + LXC 400) und
|
||
ist abgeschlossen. **Dass er hier erneut auftrat, ist genau die Begründung für Punkt 4:**
|
||
die Klasse wurde damals an den bekannten Stellen behoben, nie im Bestand gesucht.
|
||
- **Nicht** TP31/TP34 (Caddy) und nicht TP11 (Monitoring) — obwohl gilt: ein Monitoring, das
|
||
DB-Verbindungszeiten kennt, hätte diesen Fall Monate früher gezeigt.
|
||
- **Nicht** TP40: der HA-Recorder-Wildwuchs (Punkt 3) ist HA-seitig und ein eigenes TP.
|
||
|
||
---
|
||
|
||
### TP34 — Caddyfile via Nautobot/GC (generische Reverse-Proxy-Schnittstelle) ▸ ❌ **VERWORFEN (2026-09-08)**
|
||
> ➡️ **Fortsetzung 2026-09-22: `TP34.2 — Interne DNS-Namen als Daten`** (Gitea #73, Prosa in
|
||
> `docs/teilprojekte.md` → `### TP34.2`). Dort lebt der *Daten → gerenderte Config*-Gedanke weiter;
|
||
> **verworfen bleibt nur das Caddyfile-Rendering** (dieser Abschnitt hier).
|
||
> 🔴 **Nachtrag 2026-09-08 (verworfen):** Der Ansatz „Caddyfile via Golden Config rendern" ist
|
||
> zurückgezogen (Commits `d63dc7b`, `bca8f6e`; Gitea-Issue #24 → `status:dropped`, closed). Der
|
||
> `420.teilzeitladen.xyz`-Block wurde wieder aus dem GC-gerenderten Caddyfile entfernt; **der
|
||
> Caddyfile bleibt manuell** in `caddy/Caddyfile`. Gründe: kein zweites SSoT-Modell für Proxy-Routen
|
||
> nötig, TP37 deckt die Dienst-Doku als `ipam.Service` bereits ab, und der einzige akute Anwendungsfall
|
||
> ließ sich mit einem manuellen Block lösen. Text unten unverändert als Konzept-Historie.
|
||
|
||
> **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) ✅ · TP23 (DNS/Caddy-Voraussetzungen) ✅ Stufe 1 · 🔴 **NEU 2026-09-06: TP37 Punkt 1+2** — Caddy rendert *Config aus Daten*, die Backend-Dienste müssen als `ipam.Service` in Nautobot existieren, sonst gibt es nichts zu rendern. **TP37 vor TP34.**
|
||
- **Datenmodell (entschieden 2026-09-06):** native Modelle — Backend = `ipam.Service`, Proxy-Route = Relationship/CustomField Frontend↔Backend, GC-Job rendert den Caddyfile. Keine eigene App (konsistent zu TP44).
|
||
- **Status:** 🔜 Stub; wartet auf TP37 P1+2. Quick-Win `terpene.teilzeitladen.xyz → 10.40.20.167:8890` als manueller Caddy-Block ist **Schritt 0** des Runbooks. Plan: [`handoff-20260906-tp37-34-36-plan.md`](handoff-20260906-tp37-34-36-plan.md).
|
||
|
||
---
|
||
|
||
<a id="tp44"></a>
|
||
### TP44 — Cloudflare-Estate als Dokumentations-SSoT in Nautobot ▸ ✅ **DURCH (2026-09-10): Etappe 1 + Etappe 2 (Teil A IPAM-Prefixe, Teil B Live-Sync als täglicher Nautobot-Job)**
|
||
> **Entscheid (User, 2026-09-02):** die volle CF-Estate (Tunnel + WARP-Connector + VPC-Service +
|
||
> Hyperdrive + Workers + alle Access-Apps) als strukturierte, abfragbare SSoT in Nautobot abbilden —
|
||
> **etappenweise: manuell jetzt, Sync später.** Nautobot kann CF nicht steuern → „SSoT" heißt
|
||
> **Dokumentations-SSoT; CF bleibt operativer Master.** Detail-Handoff:
|
||
> [`handoff-20260902-cf-estate-nautobot-ssot.md`](handoff-20260902-cf-estate-nautobot-ssot.md).
|
||
|
||
- **Warum eigenes TP, nicht TP36:** TP36 ist eng auf `teilprojekte.md`-Live-Daten geschnitten; gleiche
|
||
Idee (Nautobot als Doku-SSoT), andere Dimension. TP44 = nächste freie Nummer nach TP43.
|
||
- **Modell (native `nautobot.cloud`, Nautobot 3.1.7 — keine eigene App):** `Manufacturer "Cloudflare"` →
|
||
`CloudAccount` (`393c051f…`) → `CloudResourceType ×6` → `CloudNetwork ×4` (`bf-public`/`fm`/`terpene-db`/
|
||
`bf-core-mesh`) + `CloudService ×14`; Service↔Tunnel über das Through-Modell
|
||
`cloud-service-network-assignments` (3 Rows). IDs/Ports/Hostnames/Origins je Objekt in `extra_config`.
|
||
- **Werkzeug:** `nautobot/tp44-cf-estate-seed.py` — idempotenter Seeder (Re-Run = 26/26 „existiert"),
|
||
Datenstand inline (Live-CF-Inventur 2026-09-02). Token aus `docs/nas-vlan20/stacks/210-nautobot.yml`.
|
||
- ✅ **Verifikation 2026-09-06 (live gegen die API):** CloudAccount 1 · ResourceType 6 · CloudNetwork 4 ·
|
||
CloudService 14 — unverändert seit 09-02.
|
||
- 🔴 **Gotchas:** `CloudService.cloud_networks` ist `read_only` (nur Through-Rows schreiben/lesen) ·
|
||
VPC-Service-API unter `connectivity/directory/services` (PUT, voller Body) · **VPC-Objekte tragen ihren
|
||
Identifier in `service_id`, NICHT `id`** (kostete im Sync einen Fehl-Match) · ⚠️ **DRIFT-KORREKTUR
|
||
2026-09-10:** die frühere Zeile „Repo-`CF_API_TOKEN` darf kein Workers-VPC → über den `cloudflare`-MCP"
|
||
war **falsch**. Live getestet liest der `CF_API_TOKEN` aus `~/.secrets/credentials.env` Hyperdrive **und**
|
||
VPC **und** Workers (alle 200). Der schmale Token ist `CF_DNS_API_TOKEN` (403). Der `cloudflare`-MCP ist
|
||
der **remote OAuth-MCP** (kein API-Token) und für einen serverseitigen Job ohnehin nicht nutzbar.
|
||
- ✅ **Etappe 2 Teil A (2026-09-08): IPAM-Prefixe live.** `10.40.0.0/16` (container, Route `bf-core-mesh`) +
|
||
`10.101.0.0/24` (network, Route `fm`) angelegt via `nautobot/tp44-ipam-prefixes.py`; der Seeder-Re-Run
|
||
hat die zwei `cloud-network-prefix-assignments` eingehängt (count=2, Idempotenz-Gegenprobe grün).
|
||
- ✅ **Etappe 2 Teil B (2026-09-10): Live-Sync als Nautobot-Job — DURCH.** Governance (User 2026-09-09):
|
||
periodische externe→Nautobot-Syncs leben als **Nautobot-Jobs** in `bf-infra/jobs/`, in Nautobot geplant —
|
||
**kein per-Task-Container/-Cron.** Runtime `jobs/cf_estate_sync.py` (`CFEstateSync`, ORM, read-only Pull,
|
||
403-tolerant, Verwaisung = `extra_config.stale` statt Löschen). Token = Nautobot-Secret `cf-api-token`
|
||
(Provider env → `CF_API_TOKEN` am Worker, in `210-nautobot.yml` verdrahtet). **Schedule: täglich 03:00Z**
|
||
(`ScheduledJob "CF Estate Sync – taeglich"`). Verifiziert: Dry-Run + Commit-Run sauber, **4 Networks /
|
||
14 Services, alle Endpoints lesbar, 0 Drift**. Der REST-Einmalproof `nautobot/tp44-cf-sync.py` bleibt als
|
||
workstation-seitiges Diagnose-Werkzeug liegen (nicht die kanonische Runtime).
|
||
- 🔴 **Drei load-bearing Funde (Detail-Handoff `handoff-20260910-tp44-teilb-sync-live.md`):**
|
||
(1) **Wurzel-`__init__.py` im Repo ist Pflicht** — Nautobot lädt Git-Repo-Jobs via
|
||
`pkgutil.walk_packages([GIT_ROOT])`, das Namespace-Packages (ohne `__init__.py`) NICHT entdeckt; ohne die
|
||
Datei bleibt `bf_infra.jobs` unsichtbar (Symptom: Sync SUCCESS + „No jobs were registered"). bf-infra ist
|
||
das erste Git-Repo mit `extras.job`. (2) `register_jobs()` gehört ins **Paket-`__init__`**, nicht nur ins
|
||
Submodul. (3) `Meta.has_sensitive_variables=False` nötig, sonst verweigert Nautobot das Scheduling.
|
||
- **Beziehung zu TP37:** TP44 hat **TP37 Punkt 3** (CF-Connectoren) vorweggenommen — dieselbe native-Modell-
|
||
Linie, die TP37 P1+2 (Substrat/Dienste) fortsetzt.
|
||
|
||
### TP41 — CF-Access-Reparatur + Zero-Trust-Konsolidierung ▸ ✅ **ABGESCHLOSSEN (2026-09-10)** — Auth-Kern seit 2026-09-01 komplett; **Gelis WARP-Client am 2026-09-10 via Android erfolgreich eingerichtet** (letzter Blocker weg, User-Bestätigung). Optionale Restpunkte (T6 Rate-Limit, geli-CGNAT-Policy-Cleanup) bewusst nicht gezogen; Keystore-Rotation läuft unter TP30 #7.
|
||
|
||
**Auslöser:** CF-Access zwang Christoph **und Geli** täglich zum OTP-Login (soll: 30 Tage); GitHub/Google
|
||
„funktionieren nicht". Root-Causes: Team-Domain-Rename `bf4`→`emsenhuber` (Provider-Callback veraltet),
|
||
App-Session 24h, diverse Ordnungs-/Sicherheitslecks. Vollständiger Verlauf + offene Schritte:
|
||
**[`docs/handoff-20260901-tp41.md`](handoff-20260901-tp41.md)**. Branch `tp41-cf-access-repair`.
|
||
|
||
**✅ Live erledigt:** Session 720h (Wildcard/vw/Warp-Login) · login_method-Loch zu (reusable Policy
|
||
`bf-users` = nur `email:christoph`+`email:geli`) · `8e44f5d1` von 4 Apps auf **nur Terpenfinder**
|
||
entkoppelt · Access-**Gruppen** `zt-humans`/`zt-home`/`zt-automation`, Policies zeigen darauf · **vw voll
|
||
Bypass** (Lebensader offen) + **`vw/admin` per WAF-Regel öffentlich geblockt** (CF-Edge + Mobilfunk
|
||
verifiziert) · API-Token auf das vollständige `…4148` konsolidiert (User hat Service-Tokens- + Zero-Trust-
|
||
Write ergänzt), altes `…2174` revoken.
|
||
|
||
**✅ T4 DURCH (2026-09-01, Variante B):** ein Access-Service-Token **`zt-svc`** (`65f1821a`, rotiert) über
|
||
Gruppe `zt-automation`; Terpene-Worker per `wrangler secret put TERPENE_SVC_CLIENT_ID` auf zt-svc
|
||
umgestellt (Worker-Pin `index.js:44`); `terpene-verify`+`-v2` **gelöscht**; `test_whoami` grün.
|
||
**Token-Altlast geklärt:** `b00722` = LIVE Caddy-DNS-Token (LXC130 caddy.env, SSoT), `d47435` = stale
|
||
Kopie (kein Consumer) → **User revoket `d47435`**; `credentials.env`-Kopie auf b00722 realignt.
|
||
|
||
**✅ T3 APPLIED (2026-09-01):** Default-Profil (`0197d1b3`; das custom „Cloudflare Mesh" matcht nur den
|
||
`warp_connector`) `fallback_domains += emsenhuber.net → [10.40.20.36, 10.40.20.37]`; Tunnel-Route
|
||
`10.40.0.0/16` (`d1da7c8a`) da, kein `10/8`-Exclude → routbar. ⏳ Client-Verify: WARP neu verbinden.
|
||
**✅ T2 DURCH (2026-09-01):** WAF-Regel `f26744eb` gelöscht; Caddy `vw`-Block `respond @cf_admin 404` bei
|
||
Header `CF-Connecting-IP` (LXC130). Verify: Edge `/admin`=404, lokal/WARP `/admin`=200. ✅
|
||
|
||
**✅ T5 DURCH (2026-09-01, User-bestätigt):** GitHub-Login läuft; Google-Login läuft nach dem
|
||
Consent-Screen-Fix (§A des Follow-up-Handoffs: User Type *Internal*→*External*/veröffentlicht).
|
||
Callback für beide `https://emsenhuber.cloudflareaccess.com/cdn-cgi/access/callback`.
|
||
**❌ Facebook/Meta für TP41 abgesagt (2026-09-01, User-Entscheid):** optional, kein Bedarf — Geli ist
|
||
über OTP `angelikahy@outlook.de` + Google `pillepalle670@gmail.com` voll gedeckt. **Keine Facebook-App,
|
||
kein CF-Facebook-IdP angelegt → nichts aufzuräumen.** Instagram hat ohnehin keinen CF-IdP.
|
||
**✅ App-Launcher (D1):** je eine Bookmark-Kachel vw/hass/terpenfinder, Wildcard-Kachel raus, Launcher
|
||
gestylt (Follow-up-Handoff §D, Policies unverändert).
|
||
|
||
**✅ Geli in WARP — CF-seitig nichts zu tun (2026-09-01 live geprüft):** Device-Enrollment läuft über
|
||
die App **„Warp Login App"** (`6c32fe23…`, type `warp`) → Policy `bf-users` → Gruppe **`zt-humans`**, die
|
||
Gelis beide Mails **schon enthält** → sie **darf bereits enrollen** (deshalb war „Device enrollment
|
||
permissions" nicht als fehlendes Setting zu finden). Terpenfinder-`geli`-Policy lässt sie per E-Mail durch.
|
||
✅ **Client am 2026-09-10 via Android erfolgreich eingerichtet** (WARP installiert, Team `emsenhuber`,
|
||
Login OTP `angelikahy@outlook.de` — User-Bestätigung); damit ist der einzige verbliebene Handgriff erledigt.
|
||
🔑 **Permission-Frage beantwortet:** CF-seitig **keine** nötig. Für generelle WARP/Enrollment/User-Verwaltung
|
||
per Account-Token: *Access: Apps and Policies → Edit* + *Access: Organizations, IdPs, and Groups → Edit*
|
||
(User erteilt 2026-09-01). **Seat-Verwaltung (`/access/seats`) bleibt per MCP gesperrt** — `10405 Method
|
||
not allowed for this authentication scheme` (Endpoint will User-Token, nicht Account-Token; kein Scope-Fix,
|
||
für Enroll irrelevant).
|
||
|
||
**🔻 Optionale Restpunkte (bewusst nicht gezogen, kein Blocker):** **T6** optional Rate-Limit ·
|
||
🔴 `TERPENE_ANDROID_KEYSTORE_PW`-Rotation (→ **TP30 Posten #7**, Transkript-Leak) · **Security-Cleanup:**
|
||
die `geli`-Terpenfinder-Policy hat **OR-Semantik** und enthält den breiten CGNAT-Bereich
|
||
`94.154.138.0/24` als IP-Include → jeder aus dieser geteilten Range käme **ohne Login** rein. Über WARP ist
|
||
der Egress ohnehin Cloudflare (nicht diese IP) → der IP-Include wäre dann überflüssig und entfernbar.
|
||
**d47435-Revoke ✅ · T3-WARP-Client-Verify ✅** (User, 2026-09-01).
|
||
|
||
**Abgrenzung:** hass/gitea bleiben (diese Runde) öffentlich; Terpenfinder-Human-Policies nicht angefasst;
|
||
**Gitea-Least-Privilege** = eigener künftiger TP (jedes Projekt repo-scoped Login, Admin nur Claude/Christoph).
|
||
- Bezug **TP11** (Monitoring): eine Plattenfüllstand-Warnung hätte das Wochen früher gezeigt.
|
||
|
||
### TP43 — dw-Standort-DNS-Uplink (site-spezifische Overrides) ▸ ♻️ **REOPENED 2026-09-11 → Volltext zurück in [`teilprojekte.md`](teilprojekte.md) (Abschnitt TP43)**
|
||
|
||
Kurz zwischen 2026-09-11 verworfen und am selben Tag reopened: die 420-Prämisse war gegenstandslos
|
||
(public CF-Worker, via WARP/TP41 gelöst), aber die **interne** BF-Namensauflösung über WG am dw blieb
|
||
dabei offen (`portainer.bf.emsenhuber.net` am dw nicht erreichbar). Korrigierter Scope + verworfene
|
||
420-Historie stehen jetzt aktiv in `teilprojekte.md`. Gitea-Issue #32 = `status:todo` · `thema:firewall-dns`.
|
||
|
||
### TP42 — Namens-Vereinheitlichung: ein interner Suffix bf.emsenhuber.net ▸ ✅ **DURCH (2026-09-02)** — ein interner Suffix `bf.emsenhuber.net`, `.bf.lan` raus, `.16` (adguard_bf1) abgebaut, Compliance 140/140. Offen nur der an **TP37** gekoppelte Nautobot-Doku-Teil.
|
||
|
||
**Auslöser (2026-09-01, TP41-Randbefund):** live gemessen liefern die realen Client-Resolver
|
||
`10.40.20.36`/`.37` `*.emsenhuber.net → 10.40.20.40` (Caddy) — korrekt. Daneben existiert aber ein
|
||
zweiter, gedrifteter interner Namensraum: der **Core** trägt in `/ip dns static` 11 Einträge auf
|
||
`*.bf.lan`, der **Caddyfile** doppelt jeden internen Namen als `*.bf.emsenhuber.net`, und die alten
|
||
AdGuard `.16`/`.17` antworten noch mit `nginx*.bf.emsenhuber.net` bzw. totem VLAN1 `10.40.41.123`.
|
||
Christoph: „aus emsenhuber.net wurde schon wieder bf.lan — das müssen wir ein für alle mal aus allen
|
||
Geräten rausprügeln und in Nautobot festhalten."
|
||
|
||
**Ziel:** genau **ein** interner Namensraum `emsenhuber.net` (Split-Horizon `.36/.37 → .40`). `.bf.lan`
|
||
und `.bf.emsenhuber.net` werden entfernt; Sollzustand in Nautobot dokumentiert.
|
||
|
||
**Scope (Reihenfolge = eigene Session):**
|
||
1. **Bestandsaufnahme** aller Fundstellen: Core `/ip dns static` (`*.bf.lan`), DHCP-Option
|
||
`domain`/Search-Domain je Netz, Caddyfile-Aliase (`*.bf.emsenhuber.net`), AdGuard-Rewrites `.36/.37`,
|
||
stale `.16/.17`, Geräte-`identity`.
|
||
2. **Entscheidung Zielschema** (nur `emsenhuber.net`, ggf. `bf.emsenhuber.net` als *einzige* interne
|
||
Zone — Christoph-Entscheid) und Migrationsreihenfolge ohne DNS-Loch.
|
||
3. **Umsetzen:** Core-DNS + DHCP + Caddyfile angleichen; stale `.16/.17` klären/abbauen.
|
||
4. **Nautobot:** interne Namen/Records als Sollzustand erfassen (Bezug TP37 Datenmodell).
|
||
|
||
**Abgrenzung:** kein CF-Public-DNS-Umbau (Zone `emsenhuber.net` öffentlich bleibt); kein Resolver-Swap
|
||
(`.36/.37` bleiben); `.1`-als-DNS-Reste auf Clients sind separat/tolerierbar (User: „bis jetzt kein
|
||
Problem"). **Abh.:** TP37 (Nautobot-Datenmodell) für Punkt 4; sonst nichts Offenes.
|
||
|
||
#### ✅ Bestandsaufnahme LIVE (2026-09-02) — Drift hat 80 % schon erledigt
|
||
Gegen die Geräte gemessen (MikroMCP + dig), nicht aus dem Handoff:
|
||
|
||
| Fundstelle | Handoff 09-01 | Live 09-02 |
|
||
|---|---|---|
|
||
| Core `/ip dns static` | 11× `*.bf.lan` | ✅ **0× `.bf.lan`** — 10× `*.bf.emsenhuber.net` (deckt sich mit GC-Template Z.284–293) |
|
||
| DHCP `domain` (7 Netze) | „prüfen" | `emsenhuber.net` (GC-Template Z.133 = gleich ⇒ **keine Drift**), DNS `.36/.37` |
|
||
| `.36/.37` (Soll) | aktiv | `vw.emsenhuber.net→.40` ✅ · `macmini.bf.emsenhuber.net→.20.167` ✅ · **`macmini.emsenhuber.net→leer`** ❌ |
|
||
| Caddyfile | `*.bf.emsenhuber.net`-Aliase | unverändert, **konsistent** mit Core-Static |
|
||
| `.16`/`.17` | tot | laufen (:53), liefern totes VLAN1; **kein Core-Objekt** (Firewall/NAT/DNS-Upstream/Addr-List) hängt an `.16` |
|
||
|
||
**Kernkonflikt:** DHCP verteilt `emsenhuber.net` (bare), Records leben unter `bf.emsenhuber.net` ⇒ bare `macmini` löst nicht.
|
||
|
||
#### ✅ Entscheidungen (User, 2026-09-02)
|
||
1. **Zielschema = `bf.emsenhuber.net`** (der wenig-Arbeit-Weg): DHCP-domain → `bf.emsenhuber.net`, dann
|
||
löst `macmini` bare korrekt; Core-Static + Caddyfile bleiben (tragen schon `.bf.`). Öffentliche Zone
|
||
`emsenhuber.net` bleibt getrennt.
|
||
2. **`.16` (adguard_bf1) abbauen**, **`.17` (adguard_fm2 = Standort FM) NICHT anfassen.**
|
||
|
||
#### ✅ Umsetzung DURCH (2026-09-02)
|
||
- **(A) DHCP-domain `emsenhuber.net → bf.emsenhuber.net` über GC ✅** — kein Direkt-Write:
|
||
`mikrotik_routeros.j2` Z.133 gehoben → `push-gc-template.py` (Commit `7a4068c4`, **Nebenbefund:** das
|
||
Remote-Template hing dem lokalen hinterher — es trug noch `*.bf.lan`+`router.lan`, obwohl Gerät+lokal
|
||
längst `.bf.emsenhuber.net` fuhren; der Push hat das GC-Repo an den Live-Stand angeglichen) →
|
||
GitRepo-Sync (SUCCESS) → manueller ConfigPlan **`tp42-dhcp-domain`** (`set [find] domain=…`, weil
|
||
`add` das Netz doppelt anlegen würde) → Deploy an **crs326-core** → Live **7/7 = bf.emsenhuber.net**,
|
||
Compliance **140/140, keine Drifts**. Der Job steht dauerhaft in `nautobot/gc-configplan.py`.
|
||
- **(B) `.16`-Teardown ✅** — `adguard_bf1` lag im geteilten Portainer-Stack **195 `adguard`** (Docker-LXC
|
||
`.126`, **ep2** — *nicht* ep14) **zusammen mit `adguard_fm2` (`.17`, Standort FM!)**. Deshalb **nicht**
|
||
Stack löschen, sondern compose auf nur `.17` getrimmt + Stack redeployt (Prune): `.16`-Container weg,
|
||
`.16:53` tot; **`.17` läuft** (⚠️ ~40 s mit-neugestartet durch den Redeploy — kurzer FM-DNS-Blip,
|
||
danach sauber). Rollback-compose gesichert. 🧹 Daten-Dirs `/opt/docker/adguard_bf1/*` bleiben als
|
||
harmlose Leichen auf dem LXC.
|
||
- **(C) Nautobot-Soll** (Scope-Punkt 4) bleibt an **TP37** gekoppelt (Datenmodell), separat — kein Blocker.
|
||
|
||
**Status:** ✅ **Namensraum vereinheitlicht (2026-09-02)** — ein interner Suffix `bf.emsenhuber.net`,
|
||
`.bf.lan` raus, `.16` abgebaut, Compliance 140/140. Offen nur der TP37-gekoppelte Nautobot-Doku-Teil.
|
||
Handoff: [`docs/handoff-20260901-tp42-naming.md`](handoff-20260901-tp42-naming.md).
|