Compliance 68/68 + TP24 (USV) ausgeloest + Doku auf den Stand nach dem Core-Cutover

Zwei Straenge, die sich dieselben Dateien teilen — deshalb ein Commit.

1) COMPLIANCE-DRIFTS BESEITIGT (User: "beseitige autonom alle") -> 68/68, 0 Drifts

Entfernt wurden die 4 verbliebenen `extra`-Drifts (der fuenfte, core 10.40.41.211,
war schon ueber TP19-vlan1-core-address gefallen):
- ap1 + ap2: Werks-defconf `/ip dns static router.lan -> 192.168.88.1`. Auf beiden
  APs der einzige statische Eintrag, die IP existiert im Netz nicht. Der Core hat
  sein eigenes, korrektes router.lan -> 10.40.99.1 (im Template, compliant).
- crs326-branch: Bring-up-Rest SVI vlan10 + 10.40.10.2, Adresse zuerst.

Richtung bewusst "am Geraet entfernen" statt "ins Template aufnehmen" — genau so
steht es seit 2026-08-13 im Switch-Zweig des Templates. Der frueher notierte
Vorbehalt "potenzielle Rettungspfade" ist per User-Entscheid aufgehoben; der
Mgmt-Fallback aus VLAN10 auf dem branch entfaellt damit (Rueckweg = MAC-Winbox am
selben Draht).

Weg: 3 manuelle GC-ConfigPlans, kein Direkt-Write. Vorher /system backup je Geraet.
Deploy gestaffelt — erst die APs, am Geraet gegengeprueft, dann branch.

Verifikation am GERAET, nicht am Job: DNS-Statics total=0 bei unveraenderten 28
bzw. 3 WLAN-Clients; branch nur noch vlan99 + 10.40.99.2/24; Ping vom Core 4/4.
Entscheidende Gegenprobe: /interface bridge vlan/export byte-identisch — `bridge`
steht dort statisch als tagged-Member von VLAN10, das Entfernen des SVI hat also
weder Traffic-Wirkung noch neuen Drift. Danach Backup -> Intended -> Compliance,
alle 4 Geraete mit frischem Success-Datum.

Neue API-Falle, die erklaert warum es bisher kein Skript gab:
POST /plugins/golden-config/config-plan/ ist 405 — das ViewSet ist read-only.
ConfigPlans entstehen nur ueber den Job "Generate Config Plans" (plan_type=manual
+ commands-TextVar). plan_type=missing erzeugt ausserdem nur add-Zeilen, also muss
alles Entfernende zwangslaeufig ein manueller Plan sein.

Neu: nautobot/gc-configplan.py — loest Device-UUIDs einzeln per Name auf und
assertet plan.device UND config_set gegen das Ziel (die alphabetische
Sortier-Falle aus TP21.1 kann damit nicht wiederkommen), verweigert config_sets
mit <<REDACTED>> (Regel 1) und print-Zeilen, kann gestaffelt deployen
(--deploy-ids) und kapselt Backup -> Intended -> Compliance (--refresh-compliance).
Dry-Run ist Default.

2) TP16.1: GATE OFFEN, Runbook ausfuehrungsreif, Governance-Ausnahme #4

Die Bedingung "gated bis TP21 den CRS326 abdeckt" ist seit der Adoption am
2026-08-13 erfuellt. §3 neu gemessen, §4.3 von Platzhalter auf echte Werte.
Die letzte inhaltliche Unbekannte hat sich erledigt: "Rueckweg-Routing TBD" —
der Core IST L3-Gateway aller VLANs, es braucht keine Zusatzroute. wg-hub liegt
auf demselben Geraet und ist damit direktes Vorbild statt fernes Muster.

Klarstellung, weil leicht zu verwechseln: das Mesh ersetzt die Tunnel NICHT. Es
loest nur den privaten Zugang (10.40.0.0/16 per IP, inkl. ICMP); die oeffentlichen
FQDNs bleiben auf cloudflared. Relevant dabei: die Route 10.40.0.0/16 zeigt auf
bf-pve — und der faehrt derzeit nur einen Connector.

Ausnahme #4 (User 2026-08-16): die WG-Zeilen des Mesh-Bring-ups auf crs326-core
duerfen direkt gesetzt werden. Grund ist strukturell, nicht bequem — TP22-Regel 1
schliesst jede <<REDACTED>>-Sektion vom Push aus, ein Privkey ist ueber Golden
Config gar nicht deploybar. Auflage: Backup vorher, intended config danach zurueck
nach Nautobot. Scope: nur dieser Bring-up.

Damit das ein einmaliger Bootstrap bleibt, ist das Secret-Handling-Design
dokumentiert: ein privater Schluessel ist Geraete-Identitaet, keine Konfiguration
(wie ein SSH-Hostkey). Der private Teil entsteht am Geraet und geht nie weg, nur
der oeffentliche kommt in die SoT und wird in die Peer-Configs gerendert.
Stufe 2 (nautobot-secrets-providers + Vault) lohnt bei 4 Geraeten und einem Key
nicht. Nebenbefund: die WG-Peers fm/dw stehen mit hartkodierten public-keys im
Jinja-Template — Public Keys sind Daten, keine Templatezeilen.

3) TP24 — USV aus TP15 herausgeloest (User-Entscheid)

Die DC-USV war der letzte offene Rest in TP15 und hat mit Switching/Routing
sachlich nichts zu tun (eigene Einkaufs-, Verkabelungs- und Elektrik-Logik).
TP15 ist damit inhaltlich zu. TP24 traegt jetzt 48V-Bus, MeanWell/LiFePO4,
Laufzeiten, DC-Eingangs-Tabelle und die 5 offenen Entscheidungen — darunter neu
die Standortfrage, die sich durch den Core-Umzug ins UG neu stellt.

4) DOKU-AUFRAEUM-SESSION A-E abgearbeitet

- Ist-Stand-Block neu geschrieben, live gegen die Geraete geprueft. Alte Fassung
  als ausklappbare Historie, ⚠️-Provisorium entfernt.
- Reihenfolge-Abschnitt auf den echten Rest-Pfad TP5 -> TP6 eingedampft (Punkt 3
  sagte noch "Jetzt: Omada-PVID-Fix").
- Stale ap1-als-Router-Aussagen in TP3/TP4/TP15/TP16.1/TP21/TP-R korrigiert —
  fortgeschrieben, nicht ueberschrieben.
- CLAUDE.md: Mac mini .41.167 -> .20.167 (war stale, live per ARP bestaetigt),
  branch-Uplink "sw1 GE8" -> "core ether8", ALLNET-/Omada-Gotchas als Historie.
- switch/ als Archiv gekennzeichnet (Geraet ausgebaut, Runner nicht ausfuehrbar).
- omada-mcp + CF-Ingress live gegengeprueft: beides sauber, Punkt abgehakt.

Nebenbefund, eingetragen aber nicht angefasst: das CF-Tunnel-HA ist degradiert
(bf-nas und bf-pve mit je nur einem Connector), und vier tote Alt-Tunnel tragen
VLAN1-Origins — darunter die Aufloesung des "unidentifizierten VLAN1-Geraets
.41.133" aus TP19 (es ist das coolify-Origin).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-16 19:28:50 +02:00
parent f31172e4d9
commit bb2c87c68e
6 changed files with 705 additions and 79 deletions

View File

@@ -29,20 +29,20 @@ GW je VLAN = `.1` (crs326-core L3) · Mgmt VLAN99: core `.99.1`, branch `.99.2`,
|---|---|---| |---|---|---|
| ~~ALLNET Core-Switch (sw1)~~ | — | ❌ **außer Betrieb (2026-08-08)** — durch `crs326-core` (L3-Router + Core-Switch, Technik UG) ersetzt; physisch abgelöst. (Telnet-Runner `switch/` = nur noch historisch.) | | ~~ALLNET Core-Switch (sw1)~~ | — | ❌ **außer Betrieb (2026-08-08)** — durch `crs326-core` (L3-Router + Core-Switch, Technik UG) ersetzt; physisch abgelöst. (Telnet-Runner `switch/` = nur noch historisch.) |
| ~~Omada Access-Switch (sw2)~~ | — | ❌ **abgebaut 2026-07-23** (TP15 Branch-Tausch) — durch `crs326-branch` in Technik OG ersetzt, physisch entfernt | | ~~Omada Access-Switch (sw2)~~ | — | ❌ **abgebaut 2026-07-23** (TP15 Branch-Tausch) — durch `crs326-branch` in Technik OG ersetzt, physisch entfernt |
| **crs326-branch** (CRS326, Technik OG) | **10.40.99.2** (VLAN99; temp VLAN10 `.10.2`) · Bootstrap Rev4 2026-08-08 | RouterOS 7.19.5 · `admin`/`~/.secrets` (`CRS326_BRANCH_*`) · REST `http://10.40.99.2/rest` · MikroMCP `routers.yaml` (live nach MCP-Neustart) · **TP15 ✅: ersetzt sw2** (L2-Switch), volle Portbelegung (ether1 Uplink-Trunk, 2/8 NAS, 3 Drucker, 4 Dock, 5/6 Sonos, 7 macmini, 24 ap2) · sw-id RGZJ-UK0T, MAC 04:F4:1C:B4:15:58 | | **crs326-branch** (CRS326, Technik OG) | **10.40.99.2** (VLAN99; temp VLAN10 `.10.2`) · Bootstrap Rev4 2026-08-08 | RouterOS 7.19.5 · `admin`/`~/.secrets` (`CRS326_BRANCH_*`) · REST `http://10.40.99.2/rest` · MikroMCP `crs326-branch` ✅ **live** (2026-08-16 verifiziert) · **TP15 ✅: ersetzt sw2** (L2-Switch), volle Portbelegung (ether1 Uplink-Trunk, 2/8 NAS, 3 Drucker, 4 Dock, 5/6 Sonos, 7 macmini, 24 ap2) · sw-id RGZJ-UK0T, MAC 04:F4:1C:B4:15:58 |
| **crs326-core** (CRS326, Technik UG) | **10.40.99.1** (VLAN99-SVI = L3-GW; same-subnet-Fallback `10.40.10.1`) · Identity `crs326-core` | RouterOS 7.19.5 · `admin`/`~/.secrets` (`CRS326_CORE_*`) · MikroMCP routerId `crs326-core` (nach MCP-Neustart) · **TP15/TP21.1 ✅ 2026-08-08: PRODUKTIVER L3-Router** (ersetzt ap1) — Routing/DHCP (7 Server + 54 Leases)/DNS (plain `1.1.1.1`, DoH-Cloudflare-TODO)/NAT/WG-Hub (fm+dw live); WAN `vlan100`=Liwest `90.146.33.177` · sw-id 4W04-URHT, MAC 04:F4:1C:B4:17:76 | | **crs326-core** (CRS326, Technik UG) | **10.40.99.1** (VLAN99-SVI = L3-GW; same-subnet-Fallback `10.40.10.1`) · Identity `crs326-core` | RouterOS 7.19.5 · `admin`/`~/.secrets` (`CRS326_CORE_*`) · MikroMCP routerId `crs326-core` ✅ **live** (2026-08-16 verifiziert) · **TP15/TP21.1 ✅ 2026-08-08: PRODUKTIVER L3-Router** (ersetzt ap1) — Routing/DHCP (7 Server + 54 Leases)/DNS (plain `1.1.1.1`, DoH-Cloudflare-TODO)/NAT/WG-Hub (fm+dw live); WAN `vlan100`=Liwest `90.146.33.177` · sw-id 4W04-URHT, MAC 04:F4:1C:B4:17:76 |
| hAP ax² #1 (ap1) | **10.40.99.11** (VLAN99) · Identity `ap1` | RouterOS 7.23.2 · `admin`/`~/.secrets` (`ROUTER_AP1_*`) · REST `http://10.40.99.11/rest` · MikroMCP `ap1` (live nach MCP-Neustart) · **TP3-Rebuild ✅ 2026-08-08: reiner Standalone-AP, KEIN CAPsMAN** — Uplink `ether1` (PoE-Port, `poe-out=off`) an crs326-core `ether15`; 6 SSIDs dirtyvader-main/-infra/-iot/-media/-untrusted/-guest, ch36/ch1; jede SSID mit `datapath` (TP3.1-Leck gefixt) · MAC D0:EA:11:4D:40:E0 · Config-Referenz `routeros/.secrets/ap1-prov-base.rsc` | | hAP ax² #1 (ap1) | **10.40.99.11** (VLAN99) · Identity `ap1` | RouterOS 7.23.2 · `admin`/`~/.secrets` (`ROUTER_AP1_*`) · REST `http://10.40.99.11/rest` · MikroMCP `ap1` ✅ live · **TP3-Rebuild ✅ 2026-08-08: reiner Standalone-AP, KEIN CAPsMAN** — Uplink `ether1` (PoE-Port, `poe-out=off`) an crs326-core `ether15`; 6 SSIDs dirtyvader-main/-infra/-iot/-media/-untrusted/-guest, ch36/ch1; jede SSID mit `datapath` (TP3.1-Leck gefixt) · MAC D0:EA:11:4D:40:E0 · Config-Referenz `routeros/.secrets/ap1-prov-base.rsc` |
| hAP ax² #2 (ap2) | **10.40.99.12** (VLAN99) · Identity `ap2` | RouterOS 7.23.2 · `admin`/`~/.secrets` (`ROUTER_AP2_*`) · REST `http://10.40.99.12/rest` · MikroMCP `ap2` · **TP3-Rebuild ✅ 2026-08-08: reiner Standalone-AP** — Uplink `ether1` (PoE) an crs326-branch `ether24`; identisch zu ap1, ch149/ch11 · MAC D0:EA:11:4D:40:3A · Config `routeros/.secrets/ap2-prov-base.rsc` | | hAP ax² #2 (ap2) | **10.40.99.12** (VLAN99) · Identity `ap2` | RouterOS 7.23.2 · `admin`/`~/.secrets` (`ROUTER_AP2_*`) · REST `http://10.40.99.12/rest` · MikroMCP `ap2` · **TP3-Rebuild ✅ 2026-08-08: reiner Standalone-AP** — Uplink `ether1` (PoE) an crs326-branch `ether24`; identisch zu ap1, ch149/ch11 · MAC D0:EA:11:4D:40:3A · Config `routeros/.secrets/ap2-prov-base.rsc` |
| **🔑 Einheitliches Admin-PW (2026-08-08)** | alle 4 MikroTik (core/branch/ap1/ap2) | user `admin`, gleiches PW (`~/.secrets/credentials.env` → `ROUTER_*_PASS`/`CRS326_*_PASS`; „Überlebensversicherung", User-Vorgabe). Werks-Defaults ap1/ap2 damit obsolet. | | **🔑 Einheitliches Admin-PW (2026-08-08)** | alle 4 MikroTik (core/branch/ap1/ap2) | user `admin`, gleiches PW (`~/.secrets/credentials.env` → `ROUTER_*_PASS`/`CRS326_*_PASS`; „Überlebensversicherung", User-Vorgabe). Werks-Defaults ap1/ap2 damit obsolet. |
| NAS (nas-bf, DS923) | 10.40.20.10 | SSH `adm.christoph@10.40.20.10` (kein PW nötig) · Docker (Portainer endpointId=14) | | NAS (nas-bf, DS923) | 10.40.20.10 | SSH `adm.christoph@10.40.20.10` (kein PW nötig) · Docker (Portainer endpointId=14) |
| platform-pg (LXC 105, pve) | 10.40.20.20 | SSH via pve (`pct exec 105 --`) · **TP20 ✅ 2026-07-16: PG16, DB `nautobot`, Creds `postgres/.secrets`** | | platform-pg (LXC 105, pve) | 10.40.20.20 | SSH via pve (`pct exec 105 --`) · **TP20 ✅ 2026-07-16: PG16, DB `nautobot`, Creds `postgres/.secrets`** |
| Nautobot (Portainer-Stack, NAS) | 10.40.20.30 | Portainer ep14, Stack Id 203 · **TP21 ✅ 2026-07-16: `ghcr.io/nautobot/nautobot:3.1.7`, DB=platform-pg, Creds in `docs/nas-vlan20/stacks/210-nautobot.yml` (gitignored)** — Golden-Config-App/Device-Adoption noch offen | | Nautobot (Portainer-Stack, NAS) | 10.40.20.30 | Portainer ep14, Stack Id 203 · **TP21 ✅ 2026-07-16: `ghcr.io/nautobot/nautobot:3.1.7`, DB=platform-pg, Creds in `docs/nas-vlan20/stacks/210-nautobot.yml` (gitignored)** · **TP22 ✅ 2026-08-13: Golden-Config-App + Device-Adoption DURCH** — alle 4 MikroTik adoptiert, Deploy-Pipeline scharf (Image `3.1.7-gc9`), **68/68 Compliance-Checks grün / 0 Drifts** (2026-08-16) |
| ~~Omada-Controller~~ | — | ❌ **entfernt 2026-07-16** (User hat den Container gelöscht — overkill für 1 Switch auf der Abschussliste, s. TP2.1/TP15) | | ~~Omada-Controller~~ | — | ❌ **entfernt 2026-07-16** (User hat den Container gelöscht — overkill für 1 Switch auf der Abschussliste, s. TP2.1/TP15) |
| ~~omada-mcp~~ | — | ❌ **entfernt 2026-07-16** — mit dem Controller gelöscht. MCP-Eintrag in Claude-Settings/`~/.claude.json` ist verwaist (Server wird nie mehr connecten), Aufräumen dort noch offen | | ~~omada-mcp~~ | — | ❌ **entfernt 2026-07-16** — mit dem Controller gelöscht. ✅ **Aufräumen erledigt/gegenstandslos (geprüft 2026-08-16):** nicht mehr in `mcpServers` registriert (nur noch als „disabled" in ein paar Projekt-Scopes = harmloser Merkposten), und **kein `omada`-Hostname mehr im CF-Ingress** |
| pve-mcp | 10.40.20.173 | **StreamableHTTP `http://10.40.20.173:3000/` (Root-Pfad, NICHT /sse)** · NAS-Stack ep14 (=nas-bf `.20.10`, NICHT LXC .126!) id 202 (vlan20-only, TP14.1 2026-07-10) · **mcp-pve v0.6.1 (Upgrade 2026-07-10: Single-Session-Bug 0.4.8 „Server already initialized" gefixt, TP14.2a)** · `~/.claude.json` ✓ | | pve-mcp | 10.40.20.173 | **StreamableHTTP `http://10.40.20.173:3000/` (Root-Pfad, NICHT /sse)** · NAS-Stack ep14 (=nas-bf `.20.10`, NICHT LXC .126!) id 202 (vlan20-only, TP14.1 2026-07-10) · **mcp-pve v0.6.1 (Upgrade 2026-07-10: Single-Session-Bug 0.4.8 „Server already initialized" gefixt, TP14.2a)** · `~/.claude.json` ✓ |
| Proxmox (pve) | 10.40.20.100 | `ssh root@10.40.20.100` (SSH-Key ✓) · **TP14 2026-07-13: Host-Mgmt VLAN1→VLAN20** (vmbr0.20 + Default-GW-Flip); `.41.100` bleibt interim ohne Route | | Proxmox (pve) | 10.40.20.100 | `ssh root@10.40.20.100` (SSH-Key ✓) · **TP14 2026-07-13: Host-Mgmt VLAN1→VLAN20** (vmbr0.20 + Default-GW-Flip); `.41.100` bleibt interim ohne Route |
| Docker-LXC (auf pve) | 10.40.20.126 | Portainer CE 2.40.0 (`https://10.40.20.126:9443`) · **TP14.2 2026-07-10: LXC 400 net0 tag=20/.20.126 (iface heißt jetzt `veth20`, NICHT eth0); Docker-Netz `vlan20`=ipvlan parent `veth20`; adguard_bf1 .20.16, adguard_fm2 .20.17** | | Docker-LXC (auf pve) | 10.40.20.126 | Portainer CE 2.40.0 (`https://10.40.20.126:9443`) · **TP14.2 2026-07-10: LXC 400 net0 tag=20/.20.126 (iface heißt jetzt `veth20`, NICHT eth0); Docker-Netz `vlan20`=ipvlan parent `veth20`; adguard_bf1 .20.16, adguard_fm2 .20.17** |
| Mac mini | 10.40.41.167 | `ssh macmini` | | Mac mini | **10.40.20.167** (VLAN20) | `ssh macmini` · ⚠️ die früher hier stehende `10.40.41.167` war **stale** (VLAN1) — live korrigiert 2026-08-16 (ARP `reachable` auf `vlan20`) |
| Arris-Kabelmodem | (Bridge-Mode, keine Mgmt-IP im BF-LAN) | Liwest-Edge · speist **`crs326-core vlan100`** (VLAN100-Transfer, WAN-IP `90.146.33.177`) — seit Core-Cutover 2026-08-08, davor via sw1 gi16 → ap1 · **FritzBox 6660 seit 2026-07-01 komplett ausgebaut** (Liwest-Tausch, kein Zurück); Telefonie nicht weitergeführt | | Arris-Kabelmodem | (Bridge-Mode, keine Mgmt-IP im BF-LAN) | Liwest-Edge · speist **`crs326-core vlan100`** (VLAN100-Transfer, WAN-IP `90.146.33.177`) — seit Core-Cutover 2026-08-08, davor via sw1 gi16 → ap1 · **FritzBox 6660 seit 2026-07-01 komplett ausgebaut** (Liwest-Tausch, kein Zurück); Telefonie nicht weitergeführt |
--- ---
@@ -50,26 +50,29 @@ GW je VLAN = `.1` (crs326-core L3) · Mgmt VLAN99: core `.99.1`, branch `.99.2`,
## Tooling ## Tooling
- **MikroTik (core/branch/ap1/ap2):** MikroMCP-Server live → `mcp__mikromcp__*`-Tools (routerId `crs326-core`/`crs326-branch`/`ap1`/`ap2`). - **MikroTik (core/branch/ap1/ap2):** MikroMCP-Server live → `mcp__mikromcp__*`-Tools (routerId `crs326-core`/`crs326-branch`/`ap1`/`ap2`).
Reads immer safe; Writes mit `dryRun:true` previewen, dann `apply_plan`. **Passwort setzen via `manage_user set-password`** (`/user set*` ist per Deny-Policy blockiert). `bridge/vlan/set` nur auf `[find … and !dynamic]`. Reads immer safe; Writes mit `dryRun:true` previewen, dann `apply_plan`. **Passwort setzen via `manage_user set-password`** (`/user set*` ist per Deny-Policy blockiert). `bridge/vlan/set` nur auf `[find … and !dynamic]`.
- **ALLNET-Switch:** kein MCP — nur Telnet-CLI (IOS-artig, FW 2.2.1). Runner: - **~~ALLNET-Switch (sw1)~~:** ❌ **Gerät ausgebaut 2026-08-08** → der Telnet-Runner unter `switch/` ist **Archiv, nicht mehr ausführbar** (kein Endpunkt). Siehe `switch/README.md`. Nicht mehr als Werkzeug anbieten.
`cd ~/dev/bf-network-r6/switch && . ./.secrets && expect cli/run-cli.exp cli/<datei>.cli` - **~~Omada-Switch (sw2)~~:** ❌ abgebaut 2026-07-23 (TP15, durch `crs326-branch` ersetzt). Controller + omada-mcp komplett gelöscht — **Omada spielt keine Rolle mehr, nie wieder aufgreifen** (User-Entscheid 2026-07-22). Aufräum-Reste ✅ geprüft 2026-08-16 (s. Tabelle oben).
Klickanleitungen in `switch/ui-guides/` für manuelle Schritte. - **Nautobot Golden Config (= der einzige legitime Config-Weg für Netzgeräte):** `nautobot/gc-configplan.py`
- **~~Omada-Switch (sw2)~~:** ❌ abgebaut 2026-07-23 (TP15, durch `crs326-branch` ersetzt). Controller + omada-mcp komplett gelöscht — Omada-Ecosystem spielt keine Rolle mehr (nur noch verwaiste MCP-Registrierung in `~/.claude.json`, Aufräumen offen). (Token = `NAUTOBOT_SUPERUSER_API_TOKEN` aus `docs/nas-vlan20/stacks/210-nautobot.yml`, gitignored).
`--job <name>` legt manuelle Pläne an (Dry-Run default), `--deploy-ids …` deployt gestaffelt,
`--refresh-compliance` fährt **Backup → Intended → Compliance** in der einzig richtigen Reihenfolge.
⚠️ **`POST /api/plugins/golden-config/config-plan/` ist `405` — das ViewSet ist read-only.** ConfigPlans
entstehen nur über den Job **`Generate Config Plans`** (`plan_type=manual` + `commands`-TextVar).
- **Docker-Management:** Portainer API `https://10.40.20.126:9443`, Key in `mcp/portainer/.env`. - **Docker-Management:** Portainer API `https://10.40.20.126:9443`, Key in `mcp/portainer/.env`.
Ablösung durch Komodo (native MCP) geplant. Ablösung durch Komodo (native MCP) geplant.
--- ---
## Kritische Gotchas ## Kritische Gotchas
- **ALLNET: nur 1 Telnet-VTY** — stale Sessions blockieren ~15 min. Sauber ausloggen (`exit exit`), nie hammern. - 📜 **~~ALLNET-Gotchas (1 Telnet-VTY, `save` nicht vergessen)~~ — gegenstandslos seit 2026-08-08** (Gerät ausgebaut). Nur noch historisch relevant, s. `switch/README.md`.
- **`save` nicht vergessen** nach ALLNET-Änderungen (sonst nach Reboot weg).
- **hAP ax² hat 128 MB Flash, kein USB** → Container NICHT auf dem Router; Container laufen auf NAS/pve. - **hAP ax² hat 128 MB Flash, kein USB** → Container NICHT auf dem Router; Container laufen auf NAS/pve.
- **VLAN20-Docker-Hosts: Host-DNS nach dem Umzug prüfen (2026-07-10, TP14.2a):** NAS + pve-LXC zeigten `/etc/resolv.conf` noch auf totes `.41.254` → `docker pull` ghcr scheitert (`EAI_AGAIN`, aber Egress ok!). Fix = `.20.1`: LXC `pct set 400 --nameserver 10.40.20.1`; **Synology-Falle: DSM-„Allgemein"-DNS ≠ was `dockerd` liest** — Interface zieht DNS per DHCP → Fix an der *Netzwerkschnittstelle* „DNS manuell". Laufende Container frieren den `127.0.0.11`-Upstream zum Erstellzeitpunkt ein (Recreate nötig). Merkregel: `docker-pull-hängt` + `dns.lookup EAI_AGAIN` aber `TCP zu ghcr-IP:443 ok` = toter Resolver, nicht Egress. - **VLAN20-Docker-Hosts: Host-DNS nach dem Umzug prüfen (2026-07-10, TP14.2a):** NAS + pve-LXC zeigten `/etc/resolv.conf` noch auf totes `.41.254` → `docker pull` ghcr scheitert (`EAI_AGAIN`, aber Egress ok!). Fix = `.20.1`: LXC `pct set 400 --nameserver 10.40.20.1`; **Synology-Falle: DSM-„Allgemein"-DNS ≠ was `dockerd` liest** — Interface zieht DNS per DHCP → Fix an der *Netzwerkschnittstelle* „DNS manuell". Laufende Container frieren den `127.0.0.11`-Upstream zum Erstellzeitpunkt ein (Recreate nötig). Merkregel: `docker-pull-hängt` + `dns.lookup EAI_AGAIN` aber `TCP zu ghcr-IP:443 ok` = toter Resolver, nicht Egress.
- **ALLNET VLAN-DB final (TP1 ✅):** 40=MEDIA, 50=UNTRUSTED; GE1/GE2/GE8 alle als Trunk mit 10/20/30/40/50/90/99/100. - 📜 **~~ALLNET VLAN-DB (TP1)~~ — historisch.** Die VLAN-Semantik gilt weiter (**40=MEDIA, 50=UNTRUSTED**), die Portangaben (GE1/GE2/GE8) beziehen sich auf ein ausgebautes Gerät.
- **ap2 hängt an `crs326-branch ether24`** (Trunk, native VLAN1 + tagged 10-99); branch-Uplink `ether1` → sw1 GE8. (Alt „Omada Port 2 = ap2" obsolet — sw2 abgebaut.) - **ap2 hängt an `crs326-branch ether24`** (Trunk, native VLAN1 + tagged 10-99); **branch-Uplink `ether1` → `crs326-core ether8`** (korrigiert 2026-08-16 — die frühere Angabe „→ sw1 GE8" stammt aus der Zeit vor dem Core-Tausch; MAC-hart belegt in TP22 Schritt 5).
- **Mesh = NEIN** (beide APs haben wired Trunk-Uplink → Loop-Risiko). - **Mesh = NEIN** (beide APs haben wired Trunk-Uplink → Loop-Risiko).
- **Beide APs = Standalone mit `vlan-filtering=yes`, KEIN CAPsMAN (TP3-Rebuild ✅ 2026-08-08):** `datapath.vlan-id` landet nur mit vlan-filtering auf der Leitung. Nur Master-Radios wifi1/wifi2 sind Bridge-Ports (Slave-VAPs forwarden darüber) → **wifi1/wifi2 müssen tagged-Member je Client-VLAN sein**. Uplink=`ether1` (PoE), VLAN1 untagged=Fallback. Jede SSID-config braucht `datapath` (sonst egress ungetaggt in VLAN1 = das alte TP3.1-Leck). **Falle bei Neu-Provisionierung:** Werks-Inline-Overrides auf wifi1/wifi2 (`configuration.ssid=MikroTik-*`, `security.*`) überschreiben das verlinkte Profil → nach `/import` mit `!configuration.ssid !security.*` leeren. Configs `routeros/.secrets/ap{1,2}-prov-base.rsc`. - **Beide APs = Standalone mit `vlan-filtering=yes`, KEIN CAPsMAN (TP3-Rebuild ✅ 2026-08-08):** `datapath.vlan-id` landet nur mit vlan-filtering auf der Leitung. Nur Master-Radios wifi1/wifi2 sind Bridge-Ports (Slave-VAPs forwarden darüber) → **wifi1/wifi2 müssen tagged-Member je Client-VLAN sein**. Uplink=`ether1` (PoE), VLAN1 untagged=Fallback. Jede SSID-config braucht `datapath` (sonst egress ungetaggt in VLAN1 = das alte TP3.1-Leck). **Falle bei Neu-Provisionierung:** Werks-Inline-Overrides auf wifi1/wifi2 (`configuration.ssid=MikroTik-*`, `security.*`) überschreiben das verlinkte Profil → nach `/import` mit `!configuration.ssid !security.*` leeren. Configs `routeros/.secrets/ap{1,2}-prov-base.rsc`.
- mgmt-VLAN-Wechsel am Switch = Lockout-Risiko: VLAN muss existieren + Port muss es tragen, in einem Schritt. - mgmt-VLAN-Wechsel am Switch = Lockout-Risiko: VLAN muss existieren + Port muss es tragen, in einem Schritt.
- **Omada ES208G = Easy Managed** → Controller-adoptierbar (ab 6.1.x), nur Basis-VLAN/QoS im Controller (kein ACL/Policy-Routing). - 📜 **~~Omada ES208G = Easy Managed~~ — gegenstandslos** (Gerät abgebaut 2026-07-23, Controller gelöscht).
- **L3-Router = `crs326-core` (TP21.1 ✅ 2026-08-08, ersetzt ap1).** Bridge vlan-filtering=yes; VLAN-SVIs `.1` je VLAN mit DHCP (7 Server / 54 Leases); DNS aktuell **plain `1.1.1.1`** (DoH-Cloudflare-Gateway-TODO, core hat kein CA). **ap1/ap2 sind NUR noch APs** — kein DHCP/DNS/NAT/Routing/WG dort. (Die frühere „TP4: ap1 ist L3-Router"-Zeile ist überholt.) - **L3-Router = `crs326-core` (TP21.1 ✅ 2026-08-08, ersetzt ap1).** Bridge vlan-filtering=yes; VLAN-SVIs `.1` je VLAN mit DHCP (7 Server / 54 Leases); DNS aktuell **plain `1.1.1.1`** (DoH-Cloudflare-Gateway-TODO, core hat kein CA). **ap1/ap2 sind NUR noch APs** — kein DHCP/DNS/NAT/Routing/WG dort. (Die frühere „TP4: ap1 ist L3-Router"-Zeile ist überholt.)
- **✅✅ Edge-Cutover (2026-07-01; Router-Umzug 2026-08-08): FritzBox ausgebaut, Arris im Bridge-Mode.** Liwest hat die FB6660 dauerhaft gegen ein Arris getauscht (physisch weg). WAN terminiert auf **`crs326-core vlan100`** (VLAN100-Transfer), core zieht die öffentliche Liwest-IP `90.146.33.177` → kein Double-NAT. **core = Public-Edge + WG-Hub** (fm/dw live), NICHT mehr ap1. Telefonie nicht weitergeführt. Runbook `routeros/wan-cutover-runbook.md` (ap1-Teile historisch). - **✅✅ Edge-Cutover (2026-07-01; Router-Umzug 2026-08-08): FritzBox ausgebaut, Arris im Bridge-Mode.** Liwest hat die FB6660 dauerhaft gegen ein Arris getauscht (physisch weg). WAN terminiert auf **`crs326-core vlan100`** (VLAN100-Transfer), core zieht die öffentliche Liwest-IP `90.146.33.177` → kein Double-NAT. **core = Public-Edge + WG-Hub** (fm/dw live), NICHT mehr ap1. Telefonie nicht weitergeführt. Runbook `routeros/wan-cutover-runbook.md` (ap1-Teile historisch).
- **hAP `device-mode=home` (ap1/ap2): scheduler + fetch blockiert** (`not allowed by device-mode`) → AP-Provisionierung idempotent **ohne** Scheduler. **`[/ping]` gibt nil in 7.23**. REST je AP `http://10.40.99.11|12/rest` (Creds = `ROUTER_AP{1,2}` in `~/.claude.json`). **Der L3-Router crs326-core ist KEIN device-mode=home** → dort gehen scheduler/fetch (z.B. DDNS-Updater). - **hAP `device-mode=home` (ap1/ap2): scheduler + fetch blockiert** (`not allowed by device-mode`) → AP-Provisionierung idempotent **ohne** Scheduler. **`[/ping]` gibt nil in 7.23**. REST je AP `http://10.40.99.11|12/rest` (Creds = `ROUTER_AP{1,2}` in `~/.claude.json`). **Der L3-Router crs326-core ist KEIN device-mode=home** → dort gehen scheduler/fetch (z.B. DDNS-Updater).

View File

@@ -1,5 +1,21 @@
# Doku-Aufräum-Session — `teilprojekte.md` auf den Stand nach dem Core-Cutover bringen # Doku-Aufräum-Session — `teilprojekte.md` auf den Stand nach dem Core-Cutover bringen
> ## ✅ AUSGEFÜHRT 2026-08-16 — Punkte A, B, C, D vollständig; E weitgehend
>
> | Punkt | Status | Was passiert ist |
> |---|---|---|
> | **A** Ist-Stand-Block | ✅ | Neu geschrieben gegen den **live geprüften** Stand (MikroMCP: Identity, `/ip/address`, Interface-States). Alte Fassung als ausklappbare Historie erhalten, ⚠️-Provisorium-Vorspann entfernt. Zusätzlich die stale IP-Block-Zeile darüber korrigiert. |
> | **B** Reihenfolge/Abhängigkeiten | ✅ | Auf den echten Rest-Pfad **TP5 → TP6** eingedampft, plus „Follow-ups nebenher", „Nachgelagert" und „Durch". Die alte 1–7-Liste samt fortgeschriebenem Vorspann liegt als Historie im `<details>`. |
> | **C** stale „ap1 = Router" | ✅ | TP4-Header, TP15 (Header + „als Nächstes" + CAPsMAN), TP16.1, TP21-Scope-Grenze korrigiert — **fortgeschrieben, nicht überschrieben**. Zusätzlich: TP3-Header (CAPsMAN → Standalone), TP-R-Sweep, TP15-Mgmt-Renumber-Zeile. |
> | **D** Kleinkram | ✅ | `omada-mcp` + CF-Ingress live gegengeprüft → **beides sauber, Punkt abgehakt**. cudy1-Formulierung an den Live-Befund angeglichen. `switch/README.md` als Archiv-Kopf angelegt. (`st-main` war bereits korrekt.) |
> | **E** CLAUDE.md + Memories | 🟡 | CLAUDE.md: Mac-mini-IP `.41.167`→**`.20.167`** (war stale), branch-Uplink `sw1 GE8`→**`core ether8`**, ALLNET-/Omada-Gotchas als Historie, Nautobot-Zeile auf TP22-Stand, MCP-„nach Neustart"-Hinweise weg. Memories: 4 überholte mit Superseded-Kopf + Index nachgezogen. **Offen: keine.** |
>
> **Zwei Live-Befunde nebenbei** (in `teilprojekte.md` eingetragen, kein Eingriff): CF-Tunnel-**HA ist degradiert**
> (`bf-nas`/`bf-pve` haben nur je 1 Connector, s. TP16) und vier tote Alt-Tunnel mit VLAN1-Origins — darunter die
> Auflösung des „unidentifizierten VLAN1-Geräts `.41.133`" aus TP19.
>
> **Scope-Änderung am selben Tag:** die DC-USV ist aus TP15 herausgelöst und heißt jetzt **TP24**.
> **Vorbereitet 2026-08-14.** Reine Doku-Session, **kein** Netz-Eingriff, kein Gerät wird angefasst. > **Vorbereitet 2026-08-14.** Reine Doku-Session, **kein** Netz-Eingriff, kein Gerät wird angefasst.
> Auslöser: `docs/teilprojekte.md` beschreibt an mehreren Stellen noch die Topologie **vor** dem > Auslöser: `docs/teilprojekte.md` beschreibt an mehreren Stellen noch die Topologie **vor** dem
> 2026-08-08 (ap1 = Router, sw1/sw2 lebendig, Omada aktiv, CAPsMAN). Das Dokument sagt über sich > 2026-08-08 (ap1 = Router, sw1/sw2 lebendig, Omada aktiv, CAPsMAN). Das Dokument sagt über sich

View File

@@ -15,23 +15,53 @@
| 90 | GUEST | 10.40.90.0/24 | nur Internet | | 90 | GUEST | 10.40.90.0/24 | nur Internet |
| 99 | MGMT | 10.40.99.0/24 | Switch/AP/Hypervisor-Mgmt | | 99 | MGMT | 10.40.99.0/24 | Switch/AP/Hypervisor-Mgmt |
| 100 | WAN | (Arris-Transfer) | zum Arris-Kabelmodem (Liwest, Bridge-Mode, Public-IP an ap1) | | 100 | WAN | (Arris-Transfer) | zum Arris-Kabelmodem (Liwest, Bridge-Mode, Public-IP an ap1) |
IP-Blöcke: `.1–10` Router/APs (ap1=.1, ap2=.2) · `.11–20` Switches (ALLNET=.11, Omada=.12). GW je VLAN = `.1`. IP-Blöcke: `.1–10` Router/Gateways · `.11–20` Switches/APs. **GW je VLAN = `.1` (crs326-core).**
Mgmt (VLAN99, Ist 2026-08-16): core `.99.1` · branch `.99.2` · ap1 `.99.11` · ap2 `.99.12`.
*(Historisch, überholt: „ap1=.1, ap2=.2 · ALLNET=.11, Omada=.12" — aus der Zeit vor dem Core-Cutover.)*
## Ist-Stand (Kurz) ## Ist-Stand (Kurz)
> ⚠️ **Die folgenden 5 Zeilen (ALLNET/Omada/ap1-als-Router/Edge-Cutover-Pfad) beschreiben die Topologie VOR dem 2026-08-08 und sind HISTORIE** — sw1/sw2 sind ausgebaut, der Omada-Stack ist gelöscht, ap1 ist reiner AP. **Aktueller Ist-Stand = Hardware-Tabelle in `CLAUDE.md` + TP21.1** (`crs326-core` = L3-Router `.99.1`, WAN auf `vlan100`). Bewusst nicht überschrieben (Historie), aber nicht als Ist lesen. **Neufassung dieses Blocks = Punkt A der vorbereiteten Aufräum-Session, `docs/doku-cleanup-session.md`** (dieser Vorspann fliegt dabei raus).
ALLNET-Core `.11` (FW2.2.1, VLANs 10/20/30/40/50/90/99/100; GE1/GE2/GE8 Trunk; **TP1 ✅ 2026-06-15**). Omada-Switch `.12` (adopted, **TP2 ✅ 2026-06-15**; Trunk-ALL auf P1+P2, Access-Profile auf P3-P8; Profile noch PVID=1 bis Omada-PVID-Fix). **Stand 2026-08-16 — live gegen die Geräte geprüft (MikroMCP), nicht aus der Doku abgeschrieben.**
Omada-Controller **`.175`** (DS923/macvlan, mbentley 6.2, 🟢 live). omada-mcp **`.176`** (pve-LXC, StreamableHTTP, 🟢 live, 84 read-Tools; Controller-API v2 intern via christoph@emsenhuber.com).
2× hAP ax² (7.23.1): ap1 `.1` (ALLNET P1) — **TP4 ✅ 2026-07-01: Router + WAN-Edge** (VLAN-Subinterfaces, DHCP, DNS, NAT, Public-Edge). ap2 `.2` (Omada P2 — Trunk). - **L3 + WAN-Edge = `crs326-core`** (CRS326-24G-2S+, RouterOS 7.19.5, Technik UG): Routing, DHCP (7 Server,
**Edge-Cutover ✅ 2026-07-01:** FB6660 physisch entfernt (Liwest-Kabelmodemtausch, kein Zurück), Arris-Kabelmodem im Bridge-Mode; WAN = `sw1 gi16 Access VLAN100 → gi15 Trunk → ap1 ether2 → vlan100 auf bridge` (**NICHT** ether1); ap1 hält Public-IP direkt (Liwest, kein CGNAT), Double-NAT weg. Telefonie war auf der FB und wird nicht weitergeführt. Vorarbeit: TP10-Kern durch (2026-06-27, WG-Hub live) → Bridge-Gate vorher gelöst. Runbook: `routeros/wan-cutover-runbook.md`. 54 Reservierungen), DNS, NAT und der WG-Hub (TP10, Peers `fm`/`dw`) laufen hier. Mgmt-SVI **`10.40.99.1`**
Kontrolle: MikroMCP (MikroTik) live · ALLNET = Klickanleitungen · Omada → Controller + omada-mcp 🟢. (vlan99), Gateway `.1` je VLAN (vlan10/20/30/40/50/90/99 alle `running`). **WAN = `vlan100`** — DHCP-Client
**Nächster Schritt (Stand 2026-08-14): TP5 — Deny-by-default am Ende von `forward`.** Alles Davorliegende ist durch: CRS326-Bring-up + L3-Umzug ap1→`crs326-core` (TP15/TP21.1 ✅ 2026-08-08), WLAN-Neuprovisionierung ohne CAPsMAN (TP3 ✅ 2026-08-08), Nautobot-Plattform + Fleet-Adoption + Deploy-Pipeline (TP21/TP22 ✅ 2026-08-13). Danach **TP6 (CAKE) = das Projektziel**. Kleine Follow-ups nebenher: DoH-Cloudflare auf core, DC-USV (TP15). ⚪ **Omada-PVID-Fix ist obsolet** (User-Entscheid 2026-07-22 — Omada spielt seit der TP15-Korrektur keine Rolle mehr im Netz, nie mehr aufgreifen). hält die öffentliche Liwest-IP **`90.146.33.177/24`** direkt vom Arris (Bridge-Mode) → kein Double-NAT.
DNS-Upstream aktuell **plain `1.1.1.1`** (DoH-Gateway offen: TP21.1 TODO 1 = TP23 Stufe 1).
- **L2-Branch = `crs326-branch`** (Technik OG, RouterOS 7.19.5): reiner L2-Switch, Mgmt **`10.40.99.2`**,
Uplink `ether1` → core `ether8`. ✅ **Bring-up-Reste entfernt (2026-08-16):** SVI `vlan10` + `10.40.10.2`
(„TEMP Bring-up VLAN10") sind weg — Mgmt läuft ausschließlich über `vlan99`.
- **WLAN = 2× hAP ax² als reine Standalone-APs** (RouterOS 7.23, **kein CAPsMAN**): ap1 **`10.40.99.11`**
(an core `ether15`), ap2 **`10.40.99.12`** (an branch `ether24`), Uplink je `ether1` (PoE-Port). 6 SSIDs
`dirtyvader-*`, jede mit eigenem `datapath` → kein ungetaggter VLAN1-Egress mehr. 802.11r/k/v aktiv
(`st-main`, seit TP3.3 fester Bestandteil des GC-Templates).
- **Ausgebaut / gelöscht:** ALLNET-`sw1` und Omada-`sw2` sind **physisch weg** (2026-08-08 bzw. 2026-07-23,
in Nautobot *Decommissioned*); Omada-Controller + `omada-mcp` gelöscht (2026-07-16); FritzBox 6660 raus
seit 2026-07-01 (Liwest-Tausch auf Arris). `cudy1` hängt noch an core `ether11`, hat aber **seit
2026-08-08 19:33 keinen Link** (live bestätigt) und steht in Nautobot auf *Offline*; `cudy2` tot.
→ **Kein aktiver VLAN1-Leak mehr**, der formale Teardown bleibt TP19.
- **Kontrolle / Governance:** MikroMCP für Reads/Debug (alle 4 Geräte in der Registry: `crs326-core`,
`crs326-branch`, `ap1`, `ap2`), **Config-Änderungen ausschließlich über Nautobot Golden Config**
(TP21/TP22 — Deploy-Pipeline scharf, **68/68 Compliance-Checks grün, 0 Drifts** seit 2026-08-16).
<details><summary>📜 Historische Fassung (Stand bis 2026-08-08, Topologie <b>vor</b> dem Core-Cutover — bewusst erhalten, nicht als Ist lesen)</summary>
> ALLNET-Core `.11` (FW2.2.1, VLANs 10/20/30/40/50/90/99/100; GE1/GE2/GE8 Trunk; **TP1 ✅ 2026-06-15**). Omada-Switch `.12` (adopted, **TP2 ✅ 2026-06-15**; Trunk-ALL auf P1+P2, Access-Profile auf P3-P8; Profile noch PVID=1 bis Omada-PVID-Fix).
> Omada-Controller **`.175`** (DS923/macvlan, mbentley 6.2, 🟢 live). omada-mcp **`.176`** (pve-LXC, StreamableHTTP, 🟢 live, 84 read-Tools; Controller-API v2 intern via christoph@emsenhuber.com).
> 2× hAP ax² (7.23.1): ap1 `.1` (ALLNET P1) — **TP4 ✅ 2026-07-01: Router + WAN-Edge** (VLAN-Subinterfaces, DHCP, DNS, NAT, Public-Edge). ap2 `.2` (Omada P2 — Trunk).
> **Edge-Cutover ✅ 2026-07-01:** FB6660 physisch entfernt (Liwest-Kabelmodemtausch, kein Zurück), Arris-Kabelmodem im Bridge-Mode; WAN = `sw1 gi16 Access VLAN100 → gi15 Trunk → ap1 ether2 → vlan100 auf bridge` (**NICHT** ether1); ap1 hält Public-IP direkt (Liwest, kein CGNAT), Double-NAT weg. Telefonie war auf der FB und wird nicht weitergeführt. Vorarbeit: TP10-Kern durch (2026-06-27, WG-Hub live) → Bridge-Gate vorher gelöst. Runbook: `routeros/wan-cutover-runbook.md`.
> Kontrolle: MikroMCP (MikroTik) live · ALLNET = Klickanleitungen · Omada → Controller + omada-mcp 🟢.
</details>
**Nächster Schritt (Stand 2026-08-14): TP5 — Deny-by-default am Ende von `forward`.** Alles Davorliegende ist durch: CRS326-Bring-up + L3-Umzug ap1→`crs326-core` (TP15/TP21.1 ✅ 2026-08-08), WLAN-Neuprovisionierung ohne CAPsMAN (TP3 ✅ 2026-08-08), Nautobot-Plattform + Fleet-Adoption + Deploy-Pipeline (TP21/TP22 ✅ 2026-08-13). Danach **TP6 (CAKE) = das Projektziel**. Kleine Follow-ups nebenher: DoH-Cloudflare auf core, DC-USV (**TP24**, seit 2026-08-16 eigener TP). ⚪ **Omada-PVID-Fix ist obsolet** (User-Entscheid 2026-07-22 — Omada spielt seit der TP15-Korrektur keine Rolle mehr im Netz, nie mehr aufgreifen).
> *Vorherige Fassung (Stand 2026-07-22, erledigt): „TP15 CRS326-Bring-up als neuer L3-Core, danach WLAN-Neuprovisionierung ohne CAPsMAN; TP5 folgt nach TP21/CRS326-Adoption."* > *Vorherige Fassung (Stand 2026-07-22, erledigt): „TP15 CRS326-Bring-up als neuer L3-Core, danach WLAN-Neuprovisionierung ohne CAPsMAN; TP5 folgt nach TP21/CRS326-Adoption."*
> 🎯 **Ziel: sauberes System „from scratch", keine Leichen/Altlasten.** Migrations-Session-Endstand = **VLAN1 + altes cudy-WLAN komplett weg** (VLAN1 nur noch Mgmt, `defconf`-Client-DHCP zurückgezogen, cudy-Uplinks aufs Ziel-Client-VLAN getaggt oder cudy abgebaut). **Unterpunkte/Aufräum-Liste bei Migration:** (a) die Interim-Härtung ap2 `wifi1/wifi2 frame-types=admit-only-vlan-tagged` (TP3.1, 2026-07-06) bleibt gültig — verschwindet automatisch mit VLAN1; (b) ~~`st-main rrm/wnm` = reines Roaming-Provisorium~~ → **kein Provisorium mehr (2026-08-13, TP3.3): Steering ist fester Bestandteil des Golden-Config-Templates + ComplianceFeature `wifi-steering`** — es war die Neuprovisionierung, die es stillschweigend verloren hatte; (c) ~~**cudy1 = verbleibender aktiver VLAN1-Leak**~~ → **ERLEDIGT (User-bestätigt + verifiziert 2026-08-14): cudy1 ist offline.** `core ether11` trägt **kein Link** (kein `R`-Flag), Nautobot-Status *Offline* (TP22 Schritt 5). Damit existiert kein aktiver VLAN1-Leak mehr; der Dual-DHCP-Race aus `project_vlan1_dhcp_incident` ist Historie. Der formale Teardown (Gerät ausbauen, Doku/Inventar) bleibt TP19. > 🎯 **Ziel: sauberes System „from scratch", keine Leichen/Altlasten.** Migrations-Session-Endstand = **VLAN1 + altes cudy-WLAN komplett weg** (VLAN1 nur noch Mgmt, `defconf`-Client-DHCP zurückgezogen, cudy-Uplinks aufs Ziel-Client-VLAN getaggt oder cudy abgebaut). **Unterpunkte/Aufräum-Liste bei Migration:** (a) die Interim-Härtung ap2 `wifi1/wifi2 frame-types=admit-only-vlan-tagged` (TP3.1, 2026-07-06) bleibt gültig — verschwindet automatisch mit VLAN1; (b) ~~`st-main rrm/wnm` = reines Roaming-Provisorium~~ → **kein Provisorium mehr (2026-08-13, TP3.3): Steering ist fester Bestandteil des Golden-Config-Templates + ComplianceFeature `wifi-steering`** — es war die Neuprovisionierung, die es stillschweigend verloren hatte; (c) ~~**cudy1 = verbleibender aktiver VLAN1-Leak**~~ → **ERLEDIGT (User-bestätigt + verifiziert 2026-08-14): cudy1 ist offline.** `core ether11` trägt **kein Link** (kein `R`-Flag), Nautobot-Status *Offline* (TP22 Schritt 5). Damit existiert kein aktiver VLAN1-Leak mehr; der Dual-DHCP-Race aus `project_vlan1_dhcp_incident` ist Historie. Der formale Teardown (Gerät ausbauen, Doku/Inventar) bleibt TP19.
> ⚠️ **HW-Constraint hAP ax²:** 1 GB RAM, aber nur **128 MB Flash + KEIN USB** → **Container auf dem Router NICHT praktikabel**. > ⚠️ **HW-Constraint hAP ax²:** 1 GB RAM, aber nur **128 MB Flash + KEIN USB** → **Container auf dem Router NICHT praktikabel**.
> Folge: **native RouterOS-Features bevorzugen** (z.B. Built-in-mDNS statt Container); Container (mDNS-Reflector, BIND9-DNS, Omada-Controller) laufen auf **NAS/pve**, nicht am hAP. > Folge: **native RouterOS-Features bevorzugen** (z.B. Built-in-mDNS statt Container); Container (mDNS-Reflector, BIND9-DNS, Omada-Controller) laufen auf **NAS/pve**, nicht am hAP.
> 🔒 **Governance-Entscheid (User, 2026-07-16, ausgeweitet 2026-07-16): Config ausschließlich über Nautobot — keine Direkt-Changes mehr an einzelnen im Netz verstreuten Geräten.** Ursprünglich als ap1/ap2-Config-Freeze bis TP21 formuliert, noch am selben Tag verallgemeinert: **jede** Netz-Gerätekonfiguration (Router, APs, künftig auch die CRS326-Switches) läuft ab jetzt exklusiv über Nautobot Golden Config (TP21), sobald das steht — keine ad-hoc MikroMCP-Writes/UI-Klicks mehr an Einzelgeräten. **TP19.1 war die letzte Ausnahme** (ap1-DNS-Fix + ap2/sw1/sw2-Mgmt-VLAN-Umzug, s.u.) — davon ist nur der risikolose ap1-Teil noch direkt gemacht worden, ap2 wurde bewusst auf „nach Nautobot" vertagt. **Macht Nautobot + alle Prerequisites (TP18→TP20→TP20.1→TP21, jetzt auch Komodo) zum aktuell kritischen Pfad**, vor TP5/TP6. Betrifft nicht Software-Migrationen auf NAS/pve (anderes Substrat) und die Frage, ob sw1/sw2 vor der CRS326-Ablösung noch angefasst werden (offen, s. TP15/TP19.1). > 🔒 **Governance-Entscheid (User, 2026-07-16, ausgeweitet 2026-07-16): Config ausschließlich über Nautobot — keine Direkt-Changes mehr an einzelnen im Netz verstreuten Geräten.** Ursprünglich als ap1/ap2-Config-Freeze bis TP21 formuliert, noch am selben Tag verallgemeinert: **jede** Netz-Gerätekonfiguration (Router, APs, künftig auch die CRS326-Switches) läuft ab jetzt exklusiv über Nautobot Golden Config (TP21), sobald das steht — keine ad-hoc MikroMCP-Writes/UI-Klicks mehr an Einzelgeräten. **TP19.1 war die letzte Ausnahme** (ap1-DNS-Fix + ap2/sw1/sw2-Mgmt-VLAN-Umzug, s.u.) — davon ist nur der risikolose ap1-Teil noch direkt gemacht worden, ap2 wurde bewusst auf „nach Nautobot" vertagt. **Macht Nautobot + alle Prerequisites (TP18→TP20→TP20.1→TP21, jetzt auch Komodo) zum aktuell kritischen Pfad**, vor TP5/TP6. Betrifft nicht Software-Migrationen auf NAS/pve (anderes Substrat) und die Frage, ob sw1/sw2 vor der CRS326-Ablösung noch angefasst werden (offen, s. TP15/TP19.1).
> ✅ **Genehmigte Ausnahme (User, 2026-07-17): der zentrale CF-Tunnel-Zugang auf ap1 (TP16/TP16.1).** Alle für den zentralen Cloudflare-Zugang **auf ap1** notwendigen (Vor-)Arbeiten dürfen direkt via MikroMCP/CF-API gemacht werden, ohne auf Nautobot GC zu warten — konkret die nativen WireGuard-Mesh-Zeilen aus TP16.1 (und analoge cloudflared/WG-Anpassungen am ap1-Edge). Begründung: Remote-Erreichbarkeit ist die Voraussetzung, um überhaupt sicher am Netz zu arbeiten (Henne-Ei zu Nautobot). **Scope-Grenze:** Die Ausnahme gilt **nur für ap1**. Sobald der zentrale Zugang später auf die **CRS326** wandert (TP15), gilt die Ausnahme dafür **vorerst NICHT** — CRS326-Config läuft dann regulär über Nautobot GC (oder braucht eine eigene, dann neu zu erteilende Ausnahme). Nachtrag der ap1-Ausnahmezeilen in die GC als Follow-up, sobald Nautobot steht. > ✅ **Genehmigte Ausnahme (User, 2026-07-17): der zentrale CF-Tunnel-Zugang auf ap1 (TP16/TP16.1).** Alle für den zentralen Cloudflare-Zugang **auf ap1** notwendigen (Vor-)Arbeiten dürfen direkt via MikroMCP/CF-API gemacht werden, ohne auf Nautobot GC zu warten — konkret die nativen WireGuard-Mesh-Zeilen aus TP16.1 (und analoge cloudflared/WG-Anpassungen am ap1-Edge). Begründung: Remote-Erreichbarkeit ist die Voraussetzung, um überhaupt sicher am Netz zu arbeiten (Henne-Ei zu Nautobot). **Scope-Grenze:** Die Ausnahme gilt **nur für ap1**. Sobald der zentrale Zugang später auf die **CRS326** wandert (TP15), gilt die Ausnahme dafür **vorerst NICHT** — CRS326-Config läuft dann regulär über Nautobot GC (oder braucht eine eigene, dann neu zu erteilende Ausnahme). Nachtrag der ap1-Ausnahmezeilen in die GC als Follow-up, sobald Nautobot steht.
> ✅ **Genehmigte Ausnahme #4 (User, 2026-08-16): die WireGuard-Zeilen des Cloudflare-Mesh-Bring-ups auf `crs326-core` (TP16.1).** Dürfen direkt via MikroMCP/REST gesetzt werden. **Begründung ist strukturell, nicht bequem:** TP22-Regel 1 schließt jede Sektion mit `<<REDACTED>>` generell vom GC-Push aus, und `/interface wireguard` ist genau so eine — ein privater Schlüssel ist über Golden Config **nicht deploybar**. Damit ist die frühere TP16.1-Zusage „CRS326-Zeilen laufen regulär über GC" gegenstandslos. **Auflage:** vorher `/system backup`, danach Ist-Stand als intended config zurück nach Nautobot. **Scope-Grenze:** nur der Mesh-Bring-up auf `crs326-core` — nicht spätere WG-Änderungen, nicht andere Geräte, und sie verlängert sich nicht automatisch. Langfristige Auflösung = das Secret-Handling-Design in TP16.1 (Privkey ist Geräte-Identität, nicht Config).
> ✅ **Genehmigte Ausnahme #2 (User, 2026-07-22): der CRS326-Bring-up (TP15).** Die **Grundkonfiguration** beider CRS326 — Management-IP, Bridge, VLAN-Filtering, Trunks/Access-Ports — darf **direkt** via MikroMCP/Winbox gemacht werden. **Begründung = Henne-Ei:** Golden Config kann nicht auf ein Gerät pushen, das im Werkszustand keine erreichbare IP hat, und die GC-Templates existieren noch nicht (TP21 „offen: Templates/intended config"). **Auflage:** der so entstandene Ist-Stand wird anschließend als **intended config nach Nautobot zurückgeschrieben**, danach übernimmt GC — die Ausnahme deckt das Bootstrapping, nicht den Dauerbetrieb. **Scope-Grenze:** gilt für die beiden CRS326 im Bring-up. Sie hebt die Regel für ap1/ap2 **nicht** auf (deren Neuprovisionierung läuft weiterhin über GC) und verlängert sich nicht automatisch auf spätere CRS326-Änderungen. Damit ist die frühere Formulierung in TP16.1 („CRS326-Config läuft regulär über Nautobot GC oder braucht eine eigene, dann neu zu erteilende Ausnahme") **erfüllt** — dies ist diese Ausnahme. > ✅ **Genehmigte Ausnahme #2 (User, 2026-07-22): der CRS326-Bring-up (TP15).** Die **Grundkonfiguration** beider CRS326 — Management-IP, Bridge, VLAN-Filtering, Trunks/Access-Ports — darf **direkt** via MikroMCP/Winbox gemacht werden. **Begründung = Henne-Ei:** Golden Config kann nicht auf ein Gerät pushen, das im Werkszustand keine erreichbare IP hat, und die GC-Templates existieren noch nicht (TP21 „offen: Templates/intended config"). **Auflage:** der so entstandene Ist-Stand wird anschließend als **intended config nach Nautobot zurückgeschrieben**, danach übernimmt GC — die Ausnahme deckt das Bootstrapping, nicht den Dauerbetrieb. **Scope-Grenze:** gilt für die beiden CRS326 im Bring-up. Sie hebt die Regel für ap1/ap2 **nicht** auf (deren Neuprovisionierung läuft weiterhin über GC) und verlängert sich nicht automatisch auf spätere CRS326-Änderungen. Damit ist die frühere Formulierung in TP16.1 („CRS326-Config läuft regulär über Nautobot GC oder braucht eine eigene, dann neu zu erteilende Ausnahme") **erfüllt** — dies ist diese Ausnahme.
--- ---
@@ -87,7 +117,8 @@ Kontrolle: MikroMCP (MikroTik) live · ALLNET = Klickanleitungen · Omada → Co
- **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. - **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). - Abh.: TP1 ✅ (VLAN-Trunk am Uplink). Verwandt: TP15 (löst sw2 mittelfristig durch CRS326 ab), TP14.1 (Auslöser).
### TP3 — WLAN / CAPsMAN ▸ ✅ KERN FERTIG (2026-06-18) — `routeros/tp3-capsman.rsc` ### TP3 — WLAN (Standalone-APs) ▸ ✅ **REBUILD DURCH (2026-08-08): beide hAP ax² sind reine Standalone-APs, CAPsMAN abgeschafft** — Erstaufbau ✅ 2026-06-18 (`routeros/tp3-capsman.rsc` = **historisch**, aktuelle Configs `routeros/.secrets/ap{1,2}-prov-base.rsc`)
> ⚠️ **Lesehinweis:** die folgenden Punkte bis zum ✅-DURCH-Eintrag am Ende beschreiben die **CAPsMAN-Ära (Juni/Juli 2026)** und sind bewusst als Historie erhalten — der Ist-Stand steht im letzten Punkt dieses TPs.
- ✅ **5 sichtbare SSIDs live, beide APs, beide Bänder, je SSID→VLAN** (wifi datapath vlan-id): - ✅ **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), `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). `-untrusted`(50, WPA2+3), `-guest`(90, WPA2+3). Alt-SSIDs `install*`/`BF-Main`/`wv*` entfernt. Temp-PSK (User setzt echte).
@@ -192,7 +223,7 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
**Sicherheitsnetz:** Backups `bf-ap{1,2}-pre-nomen-20260702.backup`/`.rsc` auf den Geräten. Kontext-Memory `project_wifi_coverage` **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 ✅. + `ref_wifi_powersave_multicast`. Abh.: TP3 ✅, TP3.1 ✅, TP3.2 ✅.
### TP4 — Router-Cutover (hAP#1 = Router + WAN-Edge) ▸ ✅ FERTIG (2026-07-01) ### 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. - ✅ **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). - ✅ **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. - ✅ **DHCP:** dhcp-v10…dhcp-v99 je auf eigenem VLAN-Interface, pools .100-.200, GW+DNS = VLAN-GW.
@@ -363,7 +394,9 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
### TP14.3 — Session C: Dormante Stacks vlan20-tauglich + Referenz-Sweep ▸ ✅ Referenz-Sweep durch (2026-07-16); dormante Stacks weiter bewusst nicht deployt ### 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). - ✅ **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. - ✅ **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.
- ⏸️ **Noch offen:** `~/.claude.json` (omada-mcp-Eintrag verwaist, Server wird nie mehr connecten — Aufräumen), CF-Ingress-Rest (ob `omada`/`omada-mcp`-Hostnames dort existieren, ungeprüft). - ✅ **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). - ↪️ **Shelly-Geräte-Übersiedelung ausgegliedert → TP17** (die ~26 IoT-Shellys sind eine eigenständige WLAN-Migration, nicht Teil des Docker-Ref-Sweeps).
### TP14.4 — Session D: CF-Access-Profil für Claude ▸ 🔜 TODO (nicht cutoff-kritisch) ### TP14.4 — Session D: CF-Access-Profil für Claude ▸ 🔜 TODO (nicht cutoff-kritisch)
@@ -372,7 +405,9 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
### TP14.5 — Session E: Freigabe-Gate → TP5-Firewall ▸ ✅ ERFÜLLT (2026-08-08) — kein eigener Change ### 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. - 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.
### TP15 — HW-Refresh: Switching + Router + DC-USV ▸ 🟢 **CRS326-Bring-up + Branch-Tausch DURCH (2026-07-23)**; Core-Tausch/L3-Umzug **vorbereitet, Cutover-Config materialisiert (2026-08-05)**, Cutover = nächstes Wartungsfenster — `docs/hw-refresh-requirements.md`, Memory `project_mikrotik_commit_nautobot_gc` ### 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: - ✅ **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). - **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`.** - **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`.**
@@ -380,7 +415,7 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
- **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). - **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**. - **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). - 🐞 **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).
- **🔜 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`. - ✅ **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**. - ✅ **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. - ✅ **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). - ✅ **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).
@@ -388,11 +423,11 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
- ✅ **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). - ✅ **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`). - 📋 **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. - 📦 **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 (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**. - **🧭 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. - **🛒 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.) - ✅ **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). - ⚠️ **kein PoE** (Sonoff/PoE-Verbraucher via Injektor, falls nötig).
- **📶 WLAN bleibt hAP ax²/CAPsMAN** — kein Hardware-/Ecosystem-Wechsel. Coverage-Schwachstellen (P750W-Fall, Terrasse/Kanten) bleiben Platzierungs-/Roaming-Thema von **TP3.3**, nicht durch neue Hardware gelöst. - **📶 WLAN bleibt hAP ax²** — kein Hardware-/Ecosystem-Wechsel. ⚠️ **Überholt am 2026-07-22/2026-08-08: ~~/CAPsMAN~~** — CAPsMAN ist abgeschafft, beide APs laufen seit dem Rebuild als **Standalone-APs aus der Golden Config** (s. TP3). Die Hardware-Aussage gilt unverändert. 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. - **🧭 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). - **🔄 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".** - **📍 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".**
@@ -400,7 +435,7 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
- ✅ **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`). - ✅ **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. - ⚠️ **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. - ⚠️ **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** (unverändert gültig): MeanWell LRS-150-48 + DDR-120-48 + LiFePO4 20Ah (~€190), 48V-Bus; CRS326 DC-input passt. - **🔋 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:** - ✅ **Standort-/Ablösungs-Mapping final (User 2026-07-22) — sauberes 1:1:**
| Bereich | heute | wird abgelöst durch | | Bereich | heute | wird abgelöst durch |
|---|---|---| |---|---|---|
@@ -418,7 +453,12 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
- **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). - **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). - Abh.: TP14 ✅ (IP-Migration). Verzahnt: **TP20** (Platform-DB), **TP21** (Nautobot GC), **TP2.1** (sw2-Standalone-Recovery).
### TP16 — CF-Tunnel: Round-Robin killen + reziprokes HA ▸ ✅ KERN UMGESETZT (2026-07-10) — `docs/cf-tunnel-mapping.md §10`, #1033 ### TP16 — CF-Tunnel: Round-Robin killen + reziprokes HA ▸ ✅ KERN UMGESETZT (2026-07-10) — ⚠️ **HA aktuell degradiert (Live-Befund 2026-08-16)** — `docs/cf-tunnel-mapping.md §10`, #1033
- 🟠 **BEFUND 2026-08-16 (beim Doku-Aufräumen live über die CF-API erhoben, kein Eingriff): das reziproke HA läuft nur noch auf einem von drei Tunneln.**
- `bf-mm` = **2 Connectoren** (je 4 conns) ✅ · `bf-nas` = **1 Connector** (3 conns) 🟠 · `bf-pve` = **1 Connector** (4 conns) 🟠.
- **Bedeutung:** die Dienste hinter `bf-nas` (gitea, grafana, pve-mcp, **vw**, ds-bf, nautobot) und `bf-pve` (pve, ccu, iobroker, z2m25, portainer, **hass**, ha-mcp) hängen jeweils an **einem einzigen** cloudflared-Prozess. Fällt dessen Host aus, sind sie weg — genau der Zustand, den TP16 beseitigt hat. **Kein `530`/Round-Robin-Problem** (das bleibt strukturell gelöst), sondern **Redundanzverlust**.
- **Bekanntes Muster:** identisch zum Incident vom 2026-07-16 (macmini-Connectoren flappten unter QUIC → Fix `TUNNEL_TRANSPORT_PROTOCOL=http2`). Diesmal sind die *macmini*-Connectoren oben (bf-mm hat 2), es fehlt je **ein** Gegenstück auf NAS/pve. **Nächster Schritt (eigene Session):** auf NAS und pve nachsehen, welcher der beiden cloudflared-Container nicht läuft, und ob es der `http2`-Konventionsfall ist.
- 🧹 **Nebenbefund beim selben Sweep — Alt-Tunnel mit VLAN1-Origins (alle `down`, 0 Connectoren, daher aktuell harmlos):** `openclaw` (→ `10.40.41.147/.148/.139`), `coolify` (→ `10.40.41.133`), `johnny2`, `TeilzeitCharlie`. ⚠️ **`openclaw` beansprucht `n8n.emsenhuber.net`, das produktiv auf `bf-mm` läuft** — käme der Tunnel je wieder hoch, entstünde exakt das alte Round-Robin-Muster auf diesem Hostname. **Empfehlung: die vier toten Tunnel löschen** (user-gated, Löschen ist nicht umkehrbar) — gehört fachlich zu **TP19** (VLAN1-Retirement: `.41.133` war eines der „unidentifizierten VLAN1-Geräte" aus TP19 B1.2 — hiermit als coolify-Origin identifiziert).
> **✅ Umgesetzt (2026-07-10, eigene Session):** reziprokes **3-Tunnel-HA** live (bf-nas `70d6a4cf` / bf-pve `f7fdf3a6` / bf-mm `7a322261`), 6 Connectoren (2/Host, je Fremdhost), 19 FQDNs Loop-Smoke **0×530**, HA per Host-Ausfall-Sim bewiesen. Round-Robin-#1033 **strukturell weg**. As-Built = `docs/cf-tunnel-mapping.md §10`. **Schritt 5 Access-Strang: ~~`vw` bypass→geschützt via WARP~~ — 🔴 GESTRICHEN (User-Constraint 2026-08-14, s. TP16.2). Verbleibend offen: `hass`/`ha-mcp` 400/403; `gateway-dw` (WG→404) klären.** > **✅ Umgesetzt (2026-07-10, eigene Session):** reziprokes **3-Tunnel-HA** live (bf-nas `70d6a4cf` / bf-pve `f7fdf3a6` / bf-mm `7a322261`), 6 Connectoren (2/Host, je Fremdhost), 19 FQDNs Loop-Smoke **0×530**, HA per Host-Ausfall-Sim bewiesen. Round-Robin-#1033 **strukturell weg**. As-Built = `docs/cf-tunnel-mapping.md §10`. **Schritt 5 Access-Strang: ~~`vw` bypass→geschützt via WARP~~ — 🔴 GESTRICHEN (User-Constraint 2026-08-14, s. TP16.2). Verbleibend offen: `hass`/`ha-mcp` 400/403; `gateway-dw` (WG→404) klären.**
- **Symptom (User):** FQDNs über CF werden **sporadisch / „sehr unsicher"** aufgelöst = #1033-Muster zurück („jeden 3. Schuss eine Niete"). User hat alle 3 cloudflared-Images + Container aktualisiert/neu gestartet. - **Symptom (User):** FQDNs über CF werden **sporadisch / „sehr unsicher"** aufgelöst = #1033-Muster zurück („jeden 3. Schuss eine Niete"). User hat alle 3 cloudflared-Images + Container aktualisiert/neu gestartet.
- **✅ Root-Cause endgültig (User-Diagnose bestätigt): macvlan/ipvlan-Host-Isolation.** Ein cloudflared-Connector ist **blind für macvlan/ipvlan-Container auf seinem EIGENEN Host** (Linux-Kernel-Regel), erreicht aber Fremdhost-L2 + co-lokierte Bridge-Container. Mehrere Connectoren auf EINEM Tunnel = geteilter Ingress + CF-**Round-Robin** → jede Origin, die auf dem Host *eines* Connectors liegt, fällt bei ~**1/N** aus. **Strukturell**, nicht durch „Origins fixen" heilbar. Bsp: `omada .20.175`/`pve-mcp .20.173`/`gitea .20.157`/`grafana .20.109` = NAS-macvlan → vom NAS-Connector NICHT erreichbar. - **✅ Root-Cause endgültig (User-Diagnose bestätigt): macvlan/ipvlan-Host-Isolation.** Ein cloudflared-Connector ist **blind für macvlan/ipvlan-Container auf seinem EIGENEN Host** (Linux-Kernel-Regel), erreicht aber Fremdhost-L2 + co-lokierte Bridge-Container. Mehrere Connectoren auf EINEM Tunnel = geteilter Ingress + CF-**Round-Robin** → jede Origin, die auf dem Host *eines* Connectors liegt, fällt bei ~**1/N** aus. **Strukturell**, nicht durch „Origins fixen" heilbar. Bsp: `omada .20.175`/`pve-mcp .20.173`/`gitea .20.157`/`grafana .20.109` = NAS-macvlan → vom NAS-Connector NICHT erreichbar.
@@ -438,8 +478,22 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
- Abh.: TP14.1/14.2 ✅. Verwandt: **TP11** (CF-Tunnel-Track-B/C + Access), #1033. SSoT: `docs/cf-tunnel-mapping.md`. Memory `project_cf_tunnel_1033`. - Abh.: TP14.1/14.2 ✅. Verwandt: **TP11** (CF-Tunnel-Track-B/C + Access), #1033. SSoT: `docs/cf-tunnel-mapping.md`. Memory `project_cf_tunnel_1033`.
- ✅ **Incident 2026-07-16 (User unterwegs, WARP „spinnt", keine BF-IPs mehr) ▸ Public-FQDN-Teil gefixt:** macmini-Connectoren (`cloudflared-bfnas`/`-bfpve`) flappten live unter QUIC/UDP (Docker-Desktop-Bridge-NAT) → `bf-nas`/`bf-pve` von 2 auf 1 Connector degradiert (HA faktisch weg, nicht down). Fix: beide auf `TUNNEL_TRANSPORT_PROTOCOL=http2` umgestellt (rollend, 0 Downtime), wieder voll HA. Konvention: macmini-Connectoren künftig immer `http2`. **ICMP-Befund (getrennt, strukturell):** TCP über die WARP-Route ging überall, ICMP nicht (fm 36 %/bf 100 %/dw 100 % Loss) — cloudflared ist QUIC-Proxy, kein WireGuard. **Kurz als „bf-pve-Route wegen USV" notiert (Commit `1c1a676`) → REVIDIERT** (Begründung invertiert: bf-pve-Connectoren laufen auf NAS+macmini, nicht pve). Details: `docs/cf-tunnel-mapping.md §13`. **→ Richtungswechsel für Private Access = TP16.1.** - ✅ **Incident 2026-07-16 (User unterwegs, WARP „spinnt", keine BF-IPs mehr) ▸ Public-FQDN-Teil gefixt:** macmini-Connectoren (`cloudflared-bfnas`/`-bfpve`) flappten live unter QUIC/UDP (Docker-Desktop-Bridge-NAT) → `bf-nas`/`bf-pve` von 2 auf 1 Connector degradiert (HA faktisch weg, nicht down). Fix: beide auf `TUNNEL_TRANSPORT_PROTOCOL=http2` umgestellt (rollend, 0 Downtime), wieder voll HA. Konvention: macmini-Connectoren künftig immer `http2`. **ICMP-Befund (getrennt, strukturell):** TCP über die WARP-Route ging überall, ICMP nicht (fm 36 %/bf 100 %/dw 100 % Loss) — cloudflared ist QUIC-Proxy, kein WireGuard. **Kurz als „bf-pve-Route wegen USV" notiert (Commit `1c1a676`) → REVIDIERT** (Begründung invertiert: bf-pve-Connectoren laufen auf NAS+macmini, nicht pve). Details: `docs/cf-tunnel-mapping.md §13`. **→ Richtungswechsel für Private Access = TP16.1.**
### TP16.1 — Private Access via native Cloudflare Mesh (WireGuard) auf dem CRS326 Core-Switch ▸ 📋 Konzept+Runbook fertig (2026-07-16), **Recherche verifiziert + Runbook gehärtet (2026-07-17)**, **Ziel-Device retargeted auf CRS326 (User-Entscheid 2026-07-22)**, Scharfschalten = eigene Session, gated bis TP21 den CRS326 abdeckt — `docs/tp16.1-warp-mesh-native-crs326.md` ### TP16.1 — Private Access via native Cloudflare Mesh (WireGuard) auf `crs326-core` ▸ 🟢 **GATE OFFEN, ausführungsreif (2026-08-16)** — Scharfschalten = eigene Session — `docs/tp16.1-warp-mesh-native-crs326.md`
- ✅ **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, Router bleibt ap1/ap2 rein für L3/WLAN) 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). - ✅ **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**. - **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. - **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). - **Verworfen:** WG-Roadwarrior (User will keine Extra-App/Profil je Gerät), alle Helper-Host-ZT-Anbieter (SPOF-Verletzung).
@@ -498,13 +552,18 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
- **Opex kumulativ:** ~€135–185/J Strom — aber **getrennt gerechnet:** FM/DW laufen über **fremde Zähler**, auf Christophs Zähler landet nur der **BF-Node ≈ €50–60/J** (Gegenleistung: er hostet deren Kopie, reziprok wie TP16). - **Opex kumulativ:** ~€135–185/J Strom — aber **getrennt gerechnet:** FM/DW laufen über **fremde Zähler**, auf Christophs Zähler landet nur der **BF-Node ≈ €50–60/J** (Gegenleistung: er hostet deren Kopie, reziprok wie TP16).
- **JBOD-vs-RAID1-Ersparnis:** Strom bloß **€9–22/J** (Christophs Zähler) → *kein* Entscheidungsgrund; der Hebel ist **Capex ~€900** (nicht gekaufte Spiegelplatten) + architektonische Korrektheit. (Reicht nicht für ein BW-Upgrade €120–240/J, taugt aber als Seed für HW-Rückstellungen.) - **JBOD-vs-RAID1-Ersparnis:** Strom bloß **€9–22/J** (Christophs Zähler) → *kein* Entscheidungsgrund; der Hebel ist **Capex ~€900** (nicht gekaufte Spiegelplatten) + architektonische Korrektheit. (Reicht nicht für ein BW-Upgrade €120–240/J, taugt aber als Seed für HW-Rückstellungen.)
- **Konzeptioneller Bruch (Syno-Nachfolger):** Kein *großes resilientes NAS* mehr nötig — die kollektive 3-Site-Redundanz *ist* die Garantie. **Syno-Rolle splittet:** Hot/SMB/VM-Disks bleiben lokal (Syno **oder** ZFS auf pve, klein/schnell); **Backup/Cold wandert in Garage über BF/FM/DW** → das 9-TB-Volume wird **aufgelöst** statt ersetzt (aus 1 SPOF werden 3 geo-redundante Kopien). Backup-Tool (restic/Kopia) sprechen S3 nativ. - **Konzeptioneller Bruch (Syno-Nachfolger):** Kein *großes resilientes NAS* mehr nötig — die kollektive 3-Site-Redundanz *ist* die Garantie. **Syno-Rolle splittet:** Hot/SMB/VM-Disks bleiben lokal (Syno **oder** ZFS auf pve, klein/schnell); **Backup/Cold wandert in Garage über BF/FM/DW** → das 9-TB-Volume wird **aufgelöst** statt ersetzt (aus 1 SPOF werden 3 geo-redundante Kopien). Backup-Tool (restic/Kopia) sprechen S3 nativ.
- **Impacts auf Bestand (Kurz):** (a) **Storage-Redundanz-Philosophie kippt** — weg von SHR/RAID pro NAS, hin zu 3× JBOD; (b) **Backup-Kette** Hyper-Backup/SMB → S3-Bucket umstellen; (c) **Container-Host-System** (TP14) kriegt Garage als LXC/Container je Site, aber auf **rohen Platten**, nicht auf bestehenden SHR-Volumes; (d) **Upload-BW** wird Durchsatz-/Rebuild-Limit; (e) **HW-Planung** verzahnt mit TP15 (Node-HW, DC-USV, pve-Standort), FM/DW brauchen je 1 Node. - **Impacts auf Bestand (Kurz):** (a) **Storage-Redundanz-Philosophie kippt** — weg von SHR/RAID pro NAS, hin zu 3× JBOD; (b) **Backup-Kette** Hyper-Backup/SMB → S3-Bucket umstellen; (c) **Container-Host-System** (TP14) kriegt Garage als LXC/Container je Site, aber auf **rohen Platten**, nicht auf bestehenden SHR-Volumes; (d) **Upload-BW** wird Durchsatz-/Rebuild-Limit; (e) **HW-Planung** verzahnt mit TP15 (Node-HW, pve-Standort) und **TP24** (Stromversorgung je Site), FM/DW brauchen je 1 Node.
- **Startpfad (inkrementell/reversibel):** ① BF zuerst — Garage-Container auf pve, 2 HDDs JBOD, Backups lokal auf Bucket (sofort Nutzen, noch keine Redundanz). ② FM/DW nachziehen (Mini-PC + 2 Platten), RF→3, ab jetzt geo-redundant+HA. ③ Syno entlasten (Backup-Last abgeben). - **Startpfad (inkrementell/reversibel):** ① BF zuerst — Garage-Container auf pve, 2 HDDs JBOD, Backups lokal auf Bucket (sofort Nutzen, noch keine Redundanz). ② FM/DW nachziehen (Mini-PC + 2 Platten), RF→3, ab jetzt geo-redundant+HA. ③ Syno entlasten (Backup-Last abgeben).
- **Offen (Modell scharfstellen):** realer Strompreis; ob **FM/DW eigene Nodes stellen** oder Christoph HW hinstellt; Backup-Tool-Wahl (restic vs Kopia); ob heißes Zeug künftig auf ZFS/pve statt Syno. - **Offen (Modell scharfstellen):** realer Strompreis; ob **FM/DW eigene Nodes stellen** oder Christoph HW hinstellt; Backup-Tool-Wahl (restic vs Kopia); ob heißes Zeug künftig auf ZFS/pve statt Syno.
- Abh.: **TP10** (WG-Multi-Site = Transport, Upload-BW-Constraint), **TP14** (Container-Hosts), **TP15** (Node-HW/USV), Muster von **TP16** (reziprokes Per-Host-HA). Kein Blocker für laufende TPs. SSoT dieser Session. - Abh.: **TP10** (WG-Multi-Site = Transport, Upload-BW-Constraint), **TP14** (Container-Hosts), **TP15** (Node-HW) / **TP24** (Stromversorgung), Muster von **TP16** (reziprokes Per-Host-HA). Kein Blocker für laufende TPs. SSoT dieser Session.
### TP19 — cudy-Teardown + VLAN1-Client-Retirement ▸ ⏳ WEIT FORTGESCHRITTEN (Live-Sweep 2026-07-16: VLAN1 faktisch schon fast leer) — `docs/tp19-vlan1-cudy-teardown-runbook.md` ### TP19 — cudy-Teardown + VLAN1-Client-Retirement ▸ ⏳ WEIT FORTGESCHRITTEN — **VLAN1 ist client-frei, es geht nur noch um Aufräumen** (Live-Sweep 2026-08-16) — `docs/tp19-vlan1-cudy-teardown-runbook.md`
- ⚠️ **Eskaliert 2026-07-15:** der unter TP3.1 dokumentierte VLAN1-Leak tritt jetzt auch auf ap1/`dirtyvader-main` auf — die ap-seitige Interim-Härtung (TP3.1) mildert das Symptom, **die endgültige Lösung ist hier, TP19**: VLAN1 physisch weg = kein Leck mehr möglich. Das erhöht die Dringlichkeit dieses TPs, verschiebt aber keinen Scope hierher. - ✅ **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).
- ⚠️ **Eskaliert 2026-07-15 (historisch — durch den WLAN-Rebuild 2026-08-08 gegenstandslos):** der unter TP3.1 dokumentierte VLAN1-Leak tritt jetzt auch auf ap1/`dirtyvader-main` auf — die ap-seitige Interim-Härtung (TP3.1) mildert das Symptom, **die endgültige Lösung ist hier, TP19**: VLAN1 physisch weg = kein Leck mehr möglich. Das erhöht die Dringlichkeit dieses TPs, verschiebt aber keinen Scope hierher.
- 🟢 **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). - 🟢 **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: - 🟢 **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). - `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).
@@ -572,7 +631,8 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
### TP21 — Nautobot Golden Config (config-as-code Control-Plane) ▸ 🟢 **Plattform + Datenmodell + Templates live** (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` ### TP21 — Nautobot Golden Config (config-as-code Control-Plane) ▸ 🟢 **Plattform + Datenmodell + Templates live** (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). - **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:** 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). - **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. - 🔒 **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). - ✅ **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). - 🐛 **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).
@@ -581,13 +641,13 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
- **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. - **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). - ✅ **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). - **Locations:** neuer LocationType `Bereich` unter `Site`; `BF → Technik UG` (crs326-core, crs326-branch, ap2) und `BF → Wohnzimmer Sideboard` (ap1).
- **Devices:** ap1 = Rolle *Router* (Ist!), 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`. - **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). - **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). - **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). - **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. - 🐞 **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). - ✅ **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` (Ziel `.99.2`, 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. - ✅ **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). - ✅ **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. - **↪️ 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.
@@ -603,7 +663,7 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
- ✅ **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`). - ✅ **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). - ✅ **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. - ✅ **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`) — 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. - ℹ️ **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):** - 🔜 **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**). 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). 2. **Intended config nach Nautobot zurückschreiben** (die Direkt-Writes liefen unter der CRS326-Cutover-Governance-Ausnahme).
@@ -622,7 +682,7 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
- ✅ **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. - ✅ **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. 5. **`ap1`/`ap2` Neuprovisionierung** (WLAN-Rebuild, eigene Session) — ap1 ist Werksreset; danach greifen die `.99.11`/`.99.12`-Registry-Einträge.
### TP22 — Device-Adoption + Golden-Config Compliance-Push (RouterOS-Fleet) ▸ ✅ **DURCH (2026-08-13)** — alle 5 Schritte erledigt (Schritt 4 per Entscheid „keine Secret-Injection"); Rest = Compliance-Backlog, gehört fachlich zu TP19 (aus TP21 ausgelagert 2026-08-08) ### 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. - **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). - **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. - ✅ **Schritt 0 DURCH (2026-08-13) — Backup-Scrubbing + Repo-Neuanlage.** Vorgezogen aus Schritt 4, weil ohne Scrubbing jedes weitere Backup Secrets geleakt hätte.
@@ -659,13 +719,19 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
- **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. - **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. - ✅ **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. - 🔍 **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).** Drei neue ComplianceFeatures, alle auf Anhieb grün: - ✅ **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. - **`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. - **`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**. - 🔑 **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. - ⚠️ **`/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.** - 🐞 **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 (5 verbleibende Drifts, alle ECHTE Altlasten — bewusst NICHT eigenmächtig entfernt, weil es potenzielle Rettungspfade sind):** ap1+ap2 `/ip dns static 192.168.88.1 router.lan` (Werks-defconf); `crs326-branch` `vlan10 "TEMP Mgmt VLAN10"` + `10.40.10.2` (Bring-up-Interim, CLAUDE.md-dokumentiert); `crs326-core` `10.40.41.211` (VLAN1-Bring-up-IP). Aufräumen gehört fachlich zu **TP19** (VLAN1-Retirement) und braucht eine bewusste Entscheidung. - ✅ **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`):** - 🔒 **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.** 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. 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.
@@ -703,9 +769,37 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
- ⚠️ **802.1X löst die IoT-Identität NICHT:** Consumer-IoT (Shelly, Sonos) hat keinen 802.1X-Supplicant; der Standardweg ist **MAB** = wieder MAC-basiert → **kein Identitätsgewinn gegenüber der DHCP-Reservierung**. **TP8 bleibt richtig, aber als Sicherheitsthema** (dynamische VLAN-Zuweisung, kein MAC-Spoofing bei Managed-Geräten), nicht als DNS-Identitätsquelle. - ⚠️ **802.1X löst die IoT-Identität NICHT:** Consumer-IoT (Shelly, Sonos) hat keinen 802.1X-Supplicant; der Standardweg ist **MAB** = wieder MAC-basiert → **kein Identitätsgewinn gegenüber der DHCP-Reservierung**. **TP8 bleibt richtig, aber als Sicherheitsthema** (dynamische VLAN-Zuweisung, kein MAC-Spoofing bei Managed-Geräten), nicht als DNS-Identitätsquelle.
- **Abh.:** **TP21.1 TODO 1** (DoH auf core), **TP5** (Firewall/Bypass-Sperre — Reihenfolge s.o.), **TP16 Schritt 5** (Access-Kollision), **TP16.1** (falls WARP zuhause bleiben soll), **TP8** (nur Sicherheit). **Kein Blocker für TP5 → TP6**, aber TP5 darf die Bypass-Sperre nicht vorziehen. - **Abh.:** **TP21.1 TODO 1** (DoH auf core), **TP5** (Firewall/Bypass-Sperre — Reihenfolge s.o.), **TP16 Schritt 5** (Access-Kollision), **TP16.1** (falls WARP zuhause bleiben soll), **TP8** (nur Sicherheit). **Kein Blocker für TP5 → TP6**, aber TP5 darf die Bypass-Sperre nicht vorziehen.
### TP24 — Unterbrechungsfreie Stromversorgung (DC-USV, 48V-Bus) ▸ 🔜 KONZEPT — **aus TP15 herausgelöst (User-Entscheid 2026-08-16)** — `docs/hw-refresh-requirements.md §9`
- **Warum ein eigener TP:** Die USV hing bisher als Unterpunkt in TP15 (HW-Refresh Switching/Router) mit. TP15s Kernauftrag — CRS326-Beschaffung, Bring-up, Core-Tausch — ist seit 2026-08-08 **durch**; die Stromversorgung ist als einziger Rest übrig geblieben und hat mit Switching sachlich nichts zu tun. Sie ist ein **eigenständiges Infrastruktur-Thema mit eigener Einkaufs-, Verkabelungs- und Entscheidungslogik** (Elektrik, Batterie, Standort) und blockiert nichts auf dem kritischen Pfad. Darum eigener TP.
- **Was es ist (Begriffsklärung):** eine **Pufferbatterie für die Netz-/Infrastruktur-Hardware** — **keine** klassische AC-USV mit 230-V-Steckdosen. Ein 48-V-Gleichspannungsbus versorgt die Geräte **direkt an ihrem DC-Eingang**; die Batterie hängt am selben Bus und übernimmt bei Netzausfall ohne Umschaltlücke. Der Umweg AC→DC→AC→DC (und damit dessen Dauerverlust) entfällt.
- **Zielbild (Aufbau, unverändert gültig aus der TP15-Recherche):**
| Teil | Typ | Preis | Wirkungsgrad |
|---|---|---|---|
| Netzteil | MeanWell **LRS-150-48** (150 W, 48 V) | ~€30 | ~88 % |
| DC-USV-Modul | MeanWell **DDR-120-48** (120 W) | ~€80 | ~95 % (pass-through) |
| Batterie | **LiFePO4 12,8 V / 20 Ah** (256 Wh) | ~€50 | — |
| DC-DC 12 V | MeanWell SD-100A-12 (100 W, für NAS/pve) | ~€30 | ~85 % |
| **Summe** | | **~€190** | |
`230 V AC → LRS-150-48 → 48-V-Bus →` Switches direkt · `→ DC-DC 12 V →` DS923/pve · `→ DDR-120-48 →` LiFePO4.
- **Warum 48 V:** PoE-Technik arbeitet nativ mit 44–57 V (kein Wandler nötig), höhere Spannung = kleinere Ströme = weniger Leitungsverlust, und 48 V ist der Telecom-/USV-Standard.
- **Laufzeit (256 Wh):** ~2,5 h bei 100 W reiner DC-Last · **~5,5 h wenn nur Switch + PoE (45 W)** · ~2 h wenn ein Inverter für den Mac mini mithängt.
- **Effizienz-Argument (der eigentliche Grund für DC statt AC):** eine AC-USV verheizt bei 100 W Dauerlast ~25–40 W (≈ €66–105/Jahr), die DC-Variante ~5–12 W (≈ €13–32/Jahr) — bei praktisch gleicher Anschaffung (~€190 vs. €150–250). Der Mehrpreis amortisiert sich über den Dauerbetrieb.
- ✅ **DC-Eingang vorhanden (geprüft):** `crs326-core` + `crs326-branch` (12–57 V, DC-Jack) · ap1/ap2 hAP ax² (12–57 V, alternativ PoE-in). ⚠️ **Adapter/Sonderfall:** DS923+ (externes 12 V/7 A, 4-Pin Mini-Fit JR) · pve/N100 (12 V Hohlstecker) · **Mac mini hat gar keinen DC-Eingang** (eingebautes Netzteil → nur über Inverter).
- **Offene Entscheidungen (aus TP15 mitgenommen, unverändert offen):**
1. **Mac mini auf USV?** Braucht einen Inverter (~15–20 % Verlust + Komplexität). Vorfrage: läuft er überhaupt 24/7? Wenn nein, reicht ein Graceful-Shutdown-Script auf USV-Warnung.
2. **Standort/Verkabelung:** Der Bus soll laut Skizze im **Arbeitszimmer/Technik OG** sitzen — der L3-Core steht aber in **Technik UG**. Entweder zweiter Zweig nach unten oder Bus dorthin, wo der Core steht. **Diese Frage ist durch den Core-Umzug neu zu stellen** (die Skizze stammt aus der Zeit, als oben der Verteiler geplant war).
3. **Arris-Kabelmodem:** vorentschieden auf **Grid** (muss ohnehin am Netz bleiben, DC-Zweig nach unten wäre Aufwand für ~1–2 W).
4. **Batteriegröße:** 20 Ah als Startwert; 50 Ah wenn die gepufferte Last über Switch+AP hinaus wächst.
5. **Monitoring/Shutdown:** wie meldet die USV einen Netzausfall an NAS/pve (Signalkontakt vs. nichts)? Ohne das gibt es kein sauberes Herunterfahren, nur Durchhalten bis die Batterie leer ist.
- **Nutzen konkret:** bei Stromausfall bleiben Core-Switch, Branch-Switch und beide APs online → **Netz, WLAN und der WG-Hub überleben** (der WAN-Uplink nur, solange auch der Arris Strom hat, s. Entscheidung 3). Für NAS/pve zählt vor allem das **kontrollierte Herunterfahren** statt des harten Abschaltens.
- **Abh.:** keine harten. **TP15 ✅** (die zu versorgende Hardware steht und hat DC-Eingänge). Verwandt: **TP18** (Garage-Nodes brauchen je Site eine Stromversorgung), **TP11** (USV-Zustand ins Monitoring). **Kein Blocker für TP5/TP6.**
- **Historie:** war bis 2026-08-16 Unterpunkt von TP15 („🔋 DC-USV: MeanWell LRS-150-48 + DDR-120-48 + LiFePO4 20Ah (~€190), 48V-Bus; CRS326 DC-input passt"). Vollständige Recherche inkl. Schaltbild, Effizienzvergleich und Gerätetabelle: `docs/hw-refresh-requirements.md §9` (+ §10 D/E/F für die offenen Fragen).
### TP-R — Router-Capabilities-Sweep ▸ FERTIG (`docs/research/routeros-capabilities.md`) ### TP-R — Router-Capabilities-Sweep ▸ FERTIG (`docs/research/routeros-capabilities.md`)
- **Leitsatz: nativ vor Container, NAS vor Router** (128MB-Flash-Limit). - **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 · **CAPsMAN + 802.11r/k/v (Fast-Roaming statt Mesh)** · Back-to-Home/WireGuard. - **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. - **Auf NAS:** FreeRADIUS(+LDAP) · NPM · icinga/Grafana · CF-Tunnel.
- **Weglassen:** LoRa (keine HW) · OpenFlow · CALEA · **ZeroTier (CPU-Fresser bis ~60 % → WireGuard)** · lokales `dude`. - **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. - ✏️ **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.
@@ -714,7 +808,49 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
--- ---
## Reihenfolge / Abhängigkeiten ## Reihenfolge / Abhängigkeiten
> 🎯 **AKTUELLER KRITISCHER PFAD (Stand 2026-07-16, überschreibt die Reihenfolge unten): TP20 (Platform-Postgres) → TP21 (Nautobot) — Ziel: fertig vor CRS326-Produktivsetzung, spätestens Montag 2026-07-20.** TP18 (Garage-S3) und die Komodo-Abhängigkeit wurden am selben Tag als Blocker entfernt/entkoppelt (s. TP20), um diese Deadline zu halten — beide werden später nachgezogen, nicht mehr vorher abgewartet. **➡️ Stand-Update 2026-08-08:** TP20/TP21-Plattform steht, Core-Cutover (TP21.1) + WLAN-Rebuild (TP3) + VLAN20-Migration (TP14) sind durch. Neuer Rest-Pfad: **TP22 (Device-Adoption + Compliance-Push) → TP5 (Deny-by-default) → TP6 (CAKE, das Ziel).** Kleine Follow-ups davor: DoH-Cloudflare auf core, USV (TP15). **➡️ Stand-Update 2026-08-13 (Abschluss): TP22 ist DURCH** (alle 5 Schritte; Fleet adoptiert, Deploy-Pipeline scharf, Datenmodell as-built, Secret-Handling entschieden) → **kritischer Pfad jetzt: TP5 (Deny-by-default) → TP6 (CAKE)**. Kleine Follow-ups davor: DoH-Cloudflare auf core, DC-USV (TP15), **Doku-Aufräumen (`docs/doku-cleanup-session.md`, vorbereitet 2026-08-14 — dieses Dokument beschreibt an mehreren Stellen noch die Topologie vor dem Core-Cutover)**. ➡️ **Neu 2026-08-14: TP23 (DNS-Policy & Self-Service) hängt an TP5** — und zwar in *beide* Richtungen: TP23 braucht TP5s Firewall, **TP5 darf aber die DNS-Bypass-Sperre erst schließen, wenn TP23s Ausnahme-Oberfläche steht** (der bisherige Workaround „Client auf 1.1.1.1" *ist* dieser Bypass). Ebenfalls in TP5 gezogen: **TP7.1** (SSDP-Reflector für die macOS-Sonos-App). ~~Kabel `core ether4`→`ether2` (Sideboard/TV)~~ **entfällt (2026-08-14):** gelöst wurde am **Port**, nicht am Kabel (User-Vorgabe „Patchfeld-Nummerierung wird nicht wegen einer Config geändert") — `ether4` steht seit 08-13 auf untagged VLAN40, End-to-End-Beweis am 08-14 erbracht, s. **TP21.1 Punkt 4**. Historisch: **TP22 war das nächste Arbeitspaket** — TP17 (Shelly-Migration) ist parallel dazu vollständig abgeschlossen (alle Shellys auf VLAN30, User-bestätigt). Grund für den Nautobot-First-Ansatz: Governance-Entscheid „Config exklusiv über Nautobot" (s. oben) macht Nautobot zur Voraussetzung für praktisch jede weitere Router-/Switch-Änderung. Die Punkte 3-4 unten (Omada-PVID-Fix etc.) sind **obsolet** (User-Entscheid 2026-07-22 — Omada spielt seit TP15-Korrektur keine Rolle mehr, nie wieder aufgreifen) — ganze Liste braucht mal eine Aufräum-Session, hier nur der aktuelle Fokus vorangestellt.
### 🎯 Kritischer Pfad (Stand 2026-08-16)
```
TP5 (Deny-by-default) ──► TP6 (CAKE) ◄ Projektziel
```
1. **TP5 — Firewall & Inter-VLAN-Policy.** Runbook liegt (`docs/tp5-deny-by-default-runbook.md`), Etappe 1 ist
ein reiner Log-Lauf. **Mit im Paket:** TP7.1 (SSDP-Reflector für die macOS-Sonos-App) — gemeinsam testen,
sonst prüft man zweimal dieselbe Strecke. **Keine offenen Blocker.**
2. **TP6 — QoS/CAKE + Call-Quality-A/B-Test.** Das eigentliche Projektziel; hängt nur an TP5.
**Eine harte Reihenfolge-Regel dazwischen:** TP5 darf die **DNS-Bypass-Sperre erst schließen, wenn TP23s
Ausnahme-Oberfläche steht** — der bisherige Workaround „Client von Hand auf `1.1.1.1`" *ist* genau dieser
Bypass. Alles andere aus TP5 ist davon unabhängig und kann vorher laufen.
### Follow-ups nebenher (blockieren nichts)
- **DoH-Cloudflare auf `crs326-core`** (heute plain `1.1.1.1`) — ist zugleich TP21.1 TODO 1 **und** TP23 Stufe 1.
- **TP24 — DC-USV** (seit 2026-08-16 eigener TP, aus TP15 herausgelöst).
- **TP19 — VLAN1-Client-Retirement / cudy-Teardown:** Rest sind Media-Check, pve-`.41.100`, Inventar/DNS und
die 5 bekannten Compliance-Drifts (Bring-up-Altlasten).
- **TP3.3 — WLAN-Coverage-Kur:** 802.11k/v ist repariert (2026-08-13); offen sind Re-Walk + Dead-Spot-Kartierung.
- **TP10 —** DDNS-Auto-Updater für `bf4.emsenhuber.net` (auf dem Core möglich, `device-mode` erlaubt scheduler).
- **TP14.4 —** CF-Access-Service-Token rotieren.
- **Doku-Aufräumen** (`docs/doku-cleanup-session.md`) — Punkte A–D am 2026-08-16 abgearbeitet, E teilweise.
### Nachgelagert / research-getrieben
**TP8** (Identity/802.1X) · **TP11** (Monitoring) · **TP12** (IPv6) · **TP13** (Botschafts-WLANs) ·
**TP16.1** (natives CF-Mesh auf dem Core) · **TP16.2** (Härtung bypass-Dienste) · **TP18** (Garage-Objektspeicher) ·
**TP20.1** (Vaultwarden → Platform-PG) · **TP23** (DNS-Policy, s. Regel oben).
### ✅ Durch (Fundament — nicht mehr planen, nur nachschlagen)
TP1 · TP2 · TP2.1 · TP3 (Rebuild 2026-08-08) · TP3.1 · TP4 (Rolle seit 08-08 auf dem Core) · TP7 · TP7.2 ·
TP10 (Kern) · TP14 (+14.1/14.2/14.2a/14.3/14.5) · TP15 · TP16 (Kern) · TP17 · TP19.1 · TP20 · TP21 · TP21.1 ·
TP22 · TP-R. **Verworfen:** TP9 (Gateway-HA, User-Entscheid 2026-08-08). **Obsolet:** Omada-PVID-Fix
(User-Entscheid 2026-07-22 — „nie wieder aufgreifen").
<details><summary>📜 Historische Fassung dieses Abschnitts (bis 2026-08-14 fortgeschrieben, hier bewusst erhalten)</summary>
> 🎯 **AKTUELLER KRITISCHER PFAD (Stand 2026-07-16, überschreibt die Reihenfolge unten): TP20 (Platform-Postgres) → TP21 (Nautobot) — Ziel: fertig vor CRS326-Produktivsetzung, spätestens Montag 2026-07-20.** TP18 (Garage-S3) und die Komodo-Abhängigkeit wurden am selben Tag als Blocker entfernt/entkoppelt (s. TP20), um diese Deadline zu halten — beide werden später nachgezogen, nicht mehr vorher abgewartet. **➡️ Stand-Update 2026-08-08:** TP20/TP21-Plattform steht, Core-Cutover (TP21.1) + WLAN-Rebuild (TP3) + VLAN20-Migration (TP14) sind durch. Neuer Rest-Pfad: **TP22 (Device-Adoption + Compliance-Push) → TP5 (Deny-by-default) → TP6 (CAKE, das Ziel).** Kleine Follow-ups davor: DoH-Cloudflare auf core, USV (**TP24**). **➡️ Stand-Update 2026-08-13 (Abschluss): TP22 ist DURCH** (alle 5 Schritte; Fleet adoptiert, Deploy-Pipeline scharf, Datenmodell as-built, Secret-Handling entschieden) → **kritischer Pfad jetzt: TP5 (Deny-by-default) → TP6 (CAKE)**. Kleine Follow-ups davor: DoH-Cloudflare auf core, DC-USV (**TP24**), **Doku-Aufräumen (`docs/doku-cleanup-session.md`, vorbereitet 2026-08-14 — dieses Dokument beschreibt an mehreren Stellen noch die Topologie vor dem Core-Cutover)**. ➡️ **Neu 2026-08-14: TP23 (DNS-Policy & Self-Service) hängt an TP5** — und zwar in *beide* Richtungen: TP23 braucht TP5s Firewall, **TP5 darf aber die DNS-Bypass-Sperre erst schließen, wenn TP23s Ausnahme-Oberfläche steht** (der bisherige Workaround „Client auf 1.1.1.1" *ist* dieser Bypass). Ebenfalls in TP5 gezogen: **TP7.1** (SSDP-Reflector für die macOS-Sonos-App). ~~Kabel `core ether4`→`ether2` (Sideboard/TV)~~ **entfällt (2026-08-14):** gelöst wurde am **Port**, nicht am Kabel (User-Vorgabe „Patchfeld-Nummerierung wird nicht wegen einer Config geändert") — `ether4` steht seit 08-13 auf untagged VLAN40, End-to-End-Beweis am 08-14 erbracht, s. **TP21.1 Punkt 4**. Historisch: **TP22 war das nächste Arbeitspaket** — TP17 (Shelly-Migration) ist parallel dazu vollständig abgeschlossen (alle Shellys auf VLAN30, User-bestätigt). Grund für den Nautobot-First-Ansatz: Governance-Entscheid „Config exklusiv über Nautobot" (s. oben) macht Nautobot zur Voraussetzung für praktisch jede weitere Router-/Switch-Änderung. Die Punkte 3-4 unten (Omada-PVID-Fix etc.) sind **obsolet** (User-Entscheid 2026-07-22 — Omada spielt seit TP15-Korrektur keine Rolle mehr, nie wieder aufgreifen) — ganze Liste braucht mal eine Aufräum-Session, hier nur der aktuelle Fokus vorangestellt.
> ✅ **TP16 (CF-Tunnel Round-Robin/macvlan-Host-Isolation → reziprokes 3-Tunnel-HA)** KERN UMGESETZT 2026-07-10 (#1033 strukturell weg). Offen nur Access-Strang (WARP/`vw`) = user-gated. As-Built `docs/cf-tunnel-mapping.md §10`. > ✅ **TP16 (CF-Tunnel Round-Robin/macvlan-Host-Isolation → reziprokes 3-Tunnel-HA)** KERN UMGESETZT 2026-07-10 (#1033 strukturell weg). Offen nur Access-Strang (WARP/`vw`) = user-gated. As-Built `docs/cf-tunnel-mapping.md §10`.
1. ~~**TP1 (GE8) → TP2 (Omada Controller).**~~ ✅ erledigt 2026-06-15. 1. ~~**TP1 (GE8) → TP2 (Omada Controller).**~~ ✅ erledigt 2026-06-15.
2. ~~**TP4 (Router-Cutover L3-Infra + WAN-Edge).**~~ ✅ erledigt 2026-06-15 (L3) + 2026-07-01 (Edge-Cutover, FB physisch weg). 2. ~~**TP4 (Router-Cutover L3-Infra + WAN-Edge).**~~ ✅ erledigt 2026-06-15 (L3) + 2026-07-01 (Edge-Cutover, FB physisch weg).
@@ -726,3 +862,5 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
5. ~~**TP10 (VPN)**~~ ✅ Kern durch (2026-06-27) + Edge-Cutover (2026-07-01) → DURCH. Offen nur: DDNS-Auto-Updater. 5. ~~**TP10 (VPN)**~~ ✅ Kern durch (2026-06-27) + Edge-Cutover (2026-07-01) → DURCH. Offen nur: DDNS-Auto-Updater.
6. **Nachgelagert/Research-getrieben:** TP8 (Access), TP9 (HA), TP11 (Monitoring + CF-Tunnel-Track-B/C), TP12 (IPv6), TP13 (Spaß). 6. **Nachgelagert/Research-getrieben:** TP8 (Access), TP9 (HA), TP11 (Monitoring + CF-Tunnel-Track-B/C), TP12 (IPv6), TP13 (Spaß).
7. **Foundation (2026-07-14):** **TP15** (MikroTik-Switching / Omada-WLAN, 2× CRS326 bestellt) ∥ **TP20 (Platform-Postgres, single-site replication-ready)** → **TP21 (Nautobot Golden Config)** ∥ **TP20.1 (Vaultwarden→PG + 2-Site-HA)**. TP20 hängt an **TP18 ①** (Garage-Bucket für pgBackRest). 7. **Foundation (2026-07-14):** **TP15** (MikroTik-Switching / Omada-WLAN, 2× CRS326 bestellt) ∥ **TP20 (Platform-Postgres, single-site replication-ready)** → **TP21 (Nautobot Golden Config)** ∥ **TP20.1 (Vaultwarden→PG + 2-Site-HA)**. TP20 hängt an **TP18 ①** (Garage-Bucket für pgBackRest).
</details>

View File

@@ -1,7 +1,8 @@
# TP16.1 — Private Access via native Cloudflare Mesh (WARP Connector) auf dem CRS326 Core-Switch # TP16.1 — Private Access via native Cloudflare Mesh (WARP Connector) auf dem CRS326 Core-Switch
> **Status: Konzept + Runbook fertig (2026-07-16), Recherche verifiziert + Runbook gehärtet (2026-07-17), Ziel-Device retargeted von ap1 auf CRS326 (User-Entscheid 2026-07-22), Scharfschalten = eigene Session, gated bis TP21 den CRS326 abdeckt.** > **Status 2026-08-16: 🟢 GATE OFFEN, ausführungsreif.** `crs326-core` ist seit 2026-08-08 der produktive L3-Router und seit 2026-08-13 in Nautobot adoptiert (68/68 compliant) — die Bedingung „gated bis TP21 den CRS326 abdeckt" ist **erfüllt**. §3 am 2026-08-16 neu gemessen, §4.3 von Platzhalter auf **ausführungsreif** gezogen, **Governance-Ausnahme #4 erteilt** (WG-Zeilen direkt, weil ein Privkey über Golden Config strukturell nicht deploybar ist). Scharfschalten bleibt eine **eigene Session** — es fasst account-weite WARP-Einstellungen an.
> ⚠️ **Retarget-Hinweis (2026-07-22):** Alles unten war ursprünglich für **ap1** geschrieben und ist jetzt auf den **CRS326 Core-Switch** ummünzt. Der CRS326 ist aktuell noch Werkszustand — die konkrete Management-IP/Interface-Zuordnung für den WG-Mesh-Node ist **noch nicht getroffen** (TBD nach TP15-Bring-up). §4.3 (Config-Befehle) ist deshalb ein **Platzhalter**, keine ausführungsreifen Werte. Grund für den Retarget: TP15-Endstand (kein RB5009, Router bleibt ap1/ap2 rein für L3/WLAN) + TP21s Modell "ganzer Kabel-Fabric via RouterOS" — der CRS326 wird ohnehin Nautobot-verwaltet, ap1 bleibt Public-Edge/WLAN-Anker. > *Historie: Konzept + Runbook 2026-07-16 · Recherche verifiziert + gehärtet 2026-07-17 · Ziel-Device von ap1 auf CRS326 retargeted 2026-07-22.*
> ⚠️ **Retarget-Hinweis (2026-07-22, Stand 2026-08-16 aufgelöst):** Alles unten war ursprünglich für **ap1** geschrieben und wurde auf den **CRS326 Core-Switch** ummünzt. ~~Der CRS326 ist aktuell noch Werkszustand — Management-IP/Interface-Zuordnung TBD, §4.3 ist ein Platzhalter.~~ → **überholt:** `crs326-core` ist produktiver L3-Router mit Mgmt `10.40.99.1`, §4.3 trägt jetzt echte Werte. Grund für den Retarget: TP15-Endstand (kein RB5009, Router bleibt ap1/ap2 rein für L3/WLAN) + TP21s Modell "ganzer Kabel-Fabric via RouterOS" — der CRS326 wird ohnehin Nautobot-verwaltet, ap1 bleibt Public-Edge/WLAN-Anker.
> Selbst-tragend: eine frische Session kann das mit diesem Doc + `docs/cf-tunnel-mapping.md` straight bauen — vorausgesetzt der CRS326-Bring-up (TP15) und die Nautobot-Adoption (TP21) stehen. > Selbst-tragend: eine frische Session kann das mit diesem Doc + `docs/cf-tunnel-mapping.md` straight bauen — vorausgesetzt der CRS326-Bring-up (TP15) und die Nautobot-Adoption (TP21) stehen.
> SSoT-Kontext: `docs/cf-tunnel-mapping.md` §13/§14, Memory `project_cf_tunnel_1033`. > SSoT-Kontext: `docs/cf-tunnel-mapping.md` §13/§14, Memory `project_cf_tunnel_1033`.
> Härtungs-Delta 2026-07-17 (gilt unabhängig vom Zielgerät): Split-Tunnel auf BEIDEN Profilen (§4.1.4), vnet-Gotcha + Weg B beim Route-Umzug (§4.4), > Härtungs-Delta 2026-07-17 (gilt unabhängig vom Zielgerät): Split-Tunnel auf BEIDEN Profilen (§4.1.4), vnet-Gotcha + Weg B beim Route-Umzug (§4.4),
@@ -59,21 +60,44 @@ Ehrliche Einschränkung: **von uns noch nicht gebaut** — starker, technisch sc
[CF-Changelog „Introducing Cloudflare Mesh"](https://developers.cloudflare.com/changelog/post/2026-04-14-cloudflare-mesh/) · [CF-Changelog „Introducing Cloudflare Mesh"](https://developers.cloudflare.com/changelog/post/2026-04-14-cloudflare-mesh/) ·
[CF-Docs Mesh Routes](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-mesh/routes/) [CF-Docs Mesh Routes](https://developers.cloudflare.com/cloudflare-one/networks/connectors/cloudflare-mesh/routes/)
## 3. Ist-Stand Account (gemessen 2026-07-16, read-only) ## 3. Ist-Stand Account (**neu gemessen 2026-08-16**, read-only — CF-API + MikroMCP)
> Die Messung vom 2026-07-16 ist auf der **CF-Seite unverändert** geblieben; auf der **Geräteseite** hat
> sich alles geändert (Core-Cutover 2026-08-08, Nautobot-Adoption 2026-08-13). Beides unten getrennt.
**Cloudflare (alle Werte am 2026-08-16 gegengeprüft, identisch zu 2026-07-16):**
- Team-Domain: **`emsenhuber.cloudflareaccess.com`** → Mesh-Node-Identität = `warp_connector@emsenhuber.cloudflareaccess.com` - Team-Domain: **`emsenhuber.cloudflareaccess.com`** → Mesh-Node-Identität = `warp_connector@emsenhuber.cloudflareaccess.com`
- **Noch KEIN** `warp_connector`-Tunnel im Account (`GET /accounts/…/warp_connector` = []). - **Noch KEIN** `warp_connector`-Tunnel (`GET /accounts/…/warp_connector` = `[]`). Wichtig: der GET **antwortet**
- Device-Settings: **`use_zt_virtual_ip: false`** (Unique-Device-IP AUS — muss für Mesh AN). (kein 403) — die Plan-Frage entscheidet sich also erst am `POST` (§4.0).
- Split-Tunnel-Modus = **Exclude**; Exclude-Liste enthält u.a. **`100.64.0.0/10`** (schluckt die Mesh-Overlay-Range - `devices/settings`: **`use_zt_virtual_ip: false`** → Unique-Device-IP weiterhin **AUS**, muss für Mesh AN.
`100.96.0.0/12` → Mesh-Traffic würde aktuell NICHT durch WARP gehen). Enthält NICHT `10.0.0.0/8` → deshalb geht `gateway_proxy_enabled: true`, `gateway_udp_proxy_enabled: true` (ICMP-Flag ist Dashboard-only, s. §4.1.5).
`10.40.0.0/16` schon heute über WARP (= warum TCP funktioniert). - **Genau EIN Device-Profil** (das Default-Profil `0197d1b3-d98a-7c24-b70c-a4d89df7583b`, `service_mode_v2: warp`).
- vnet-Route `10.40.0.0/16` (vnet `1480b545…`, Route-ID **`5546dcb6-5901-4da6-8d91-b17f94703f71`**) zeigt aktuell ➡️ Der „auf BEIDEN Profilen"-Hinweis in §4.1.4 meint deshalb: **Default jetzt, Mesh-Profil sobald es in Schritt 3
auf **cfd_tunnel `bf-pve`** (`f7fdf3a6…`). Das bleibt der **Fallback**, bis Mesh bewiesen ist — nichts vorher löschen. angelegt ist** — es gibt aktuell kein zweites, auf dem man etwas vergessen könnte.
- ap1-WireGuard IST-Stand (bleibt unverändert, unabhängig vom Retarget): 1 Interface `wg-hub` (listen 51821, MTU 1412), - Split-Tunnel-Modus = **Exclude**; die Liste enthält weiterhin **`100.64.0.0/10`** (schluckt die Mesh-Overlay-Range
Peers `fm` (10.101.0.0/24) + `dw` (10.95.10.0/24) — das ist TP10, nicht Teil dieses TPs. `100.96.0.0/12`) und **kein** `10.0.0.0/8` → `10.40.0.0/16` läuft schon heute über WARP (= warum TCP geht).
- **CRS326 IST-Stand (Retarget-Ziel):** Werkszustand, noch keine WireGuard-Config, keine Management-IP zugewiesen — Nebenbefund: `192.168.0.0/16` steht drin — das ist die Erklärung für die 100 % Ping-Loss nach **dw**, unabhängig von diesem TP.
das neue Mesh-Interface (outbound zum CF-PoP, kein Listen-Port) entsteht dort auf einem sauberen Blatt, sobald - vnet-Route `10.40.0.0/16` (vnet `1480b545…` = `vnetemsenhuber`, **default**, Route-ID
der CRS326-Bring-up (TP15: Management-IP, VLAN-Filtering) steht. **`5546dcb6-5901-4da6-8d91-b17f94703f71`**) zeigt weiterhin auf **cfd_tunnel `bf-pve`** (`f7fdf3a6…`).
Bleibt der **Fallback**, bis Mesh bewiesen ist — vorher nichts löschen.
⚠️ **Neu zu wissen:** `bf-pve` fährt aktuell nur **einen** Connector (statt zwei, s. TP16-HA-Befund 2026-08-16) —
der private Pfad hängt damit heute an einem einzelnen cloudflared-Prozess. Ein weiteres Argument für das Mesh,
aber **kein** Ersatz für die HA-Reparatur der öffentlichen Tunnel.
- `CF_API_TOKEN` ist `active` (verify OK). Ob er die separate Permission **„Cloudflare One Connector: WARP"** trägt,
lässt sich nur durch den `POST` in §4.0 feststellen — genau dort schlug es am 2026-07-17 mit `10000` fehl.
**Geräteseite — die alten Zeilen hier waren überholt, Neufassung 2026-08-16:**
- ~~„CRS326 = Werkszustand, keine WireGuard-Config, keine Management-IP"~~ → **falsch seit dem Core-Cutover.**
`crs326-core` ist der **produktive L3-Router** (RouterOS 7.19.5): Mgmt `10.40.99.1` (vlan99), Gateway `.1` in
**jedem** VLAN, WAN `vlan100` mit der öffentlichen `90.146.33.177`, NAT, DHCP, DNS.
- ~~„ap1-WireGuard: 1 Interface `wg-hub`"~~ → **wg-hub liegt seit 2026-08-08 auf dem Core**, nicht mehr auf ap1
(listen 51821, MTU 1412, Peers `fm` 10.101.0.0/24 + `dw` 10.95.10.0/24, beide handshaken). Das ist TP10 und
bleibt unangetastet — aber es ist jetzt das **direkte Vorbild auf demselben Gerät**: das Mesh-Interface entsteht
daneben, nicht auf einem fremden Blatt.
- ✅ **Die offene Frage „Rückweg-Routing TBD" aus §4.3 ist damit beantwortet:** der Core *ist* der L3-Router aller
VLANs, Rückverkehr aus `10.40.0.0/16` zum Mesh-Interface braucht **keine** Zusatzroute.
- `crs326-core` ist seit 2026-08-13 in Nautobot adoptiert und compliance-verwaltet (68/68) — damit ist das
Gate „gated bis TP21 den CRS326 abdeckt" **erfüllt**.
## 4. Runbook (nächste Session — Reihenfolge) ## 4. Runbook (nächste Session — Reihenfolge)
@@ -125,31 +149,77 @@ ignorieren, wir bauen v4-only), **DNS** (CF-Resolver — ignorieren, ap1 hat eig
Registriert den Node via CF-API (macht Netz-Call), läuft danach nie wieder. **PrivateKey nach `mcp/cloudflare/.env` Registriert den Node via CF-API (macht Netz-Call), läuft danach nie wieder. **PrivateKey nach `mcp/cloudflare/.env`
o. `~/.secrets`** (nicht committen). o. `~/.secrets`** (nicht committen).
### 4.3 CRS326 nativ konfigurieren ⚠️ PLATZHALTER — noch NICHT ausführungsreif ### 4.3 `crs326-core` nativ konfigurieren ✅ ausführungsreif (Werte finalisiert 2026-08-16)
> Dieser Block war ursprünglich für ap1 geschrieben (per MikroMCP `plan_changes` dryRun → `apply_plan`). Nach dem > Der frühere Platzhalter-Vorbehalt („Management-IP/Interface-Zuordnung fehlt") ist erledigt: Zielgerät ist
> Retarget auf den CRS326 (2026-07-22) fehlen noch: die Management-IP/Interface-Zuordnung des CRS326 (TP15-Bring-up), > **`crs326-core`** (Mgmt `10.40.99.1`, MikroMCP-routerId `crs326-core`). Offen bleibt nur, was erst zur Laufzeit
> und der Zugriffsweg für Config-Changes ist dann **Nautobot Golden Config (TP21)**, nicht mehr MikroMCP direkt (die > existiert: der PrivateKey und die `100.96.x.y`-Adresse aus §4.2.
> ap1-Ausnahme gilt für den CRS326 nicht). Die folgenden Zeilen zeigen nur das **Muster** (identisch zur RouterOS-Syntax),
> `<CRS326-INTERFACE>` und die konkreten IP-Werte müssen nach dem Bring-up final werden:
``` ```
/interface wireguard add name=cf-mesh mtu=1420 private-key="<PrivateKey aus .conf>" /interface wireguard add name=cf-mesh mtu=1420 private-key="<PrivateKey aus .conf>" comment="TP16.1 Cloudflare Mesh"
/interface wireguard peers add interface=cf-mesh name=cf-pop \ /interface wireguard peers add interface=cf-mesh name=cf-pop \
public-key="bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=" \ public-key="bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=" \
endpoint-address=162.159.193.1 endpoint-port=2408 \ endpoint-address=162.159.193.1 endpoint-port=2408 \
allowed-address=0.0.0.0/0,::/0 persistent-keepalive=25s allowed-address=0.0.0.0/0,::/0 persistent-keepalive=25s
/ip address add address=<100.96.x.y>/32 interface=cf-mesh /ip address add address=<100.96.x.y>/32 interface=cf-mesh comment="TP16.1 Mesh-Overlay"
/ip route add dst-address=100.96.0.0/12 gateway=cf-mesh /ip route add dst-address=100.96.0.0/12 gateway=cf-mesh comment="TP16.1 Mesh"
``` ```
- Endpoint: falls die Config `162.159.192.x` liefert → `162.159.193.x` nehmen (Latenz-Tuning laut Guide).
- MTU 1420 aus dem Guide; gegen die TP10-Hub-MTU 1412 (auf ap1, zum Vergleich) gegenprüfen (ggf. angleichen bei Path-MTU-Problemen). - **Endpoint:** falls die Config `162.159.192.x` liefert → `162.159.193.x` nehmen (Latenz-Tuning laut Guide).
- **Rückweg-Routing TBD:** ob der CRS326 selbst L3 für alle VLANs braucht oder nur als Mesh-Endpoint mit einer Route zurück - **MTU:** 1420 aus dem Guide. Direkter Vergleichswert steht jetzt **auf demselben Gerät**: `wg-hub` läuft mit
zu ap1 (dem weiterhin bestehenden L3-Router) fungiert, ist Teil der TP15-Bring-up-Entscheidung — anders als beim **1412**. Bei Path-MTU-Symptomen (TCP baut auf, große Transfers hängen) zuerst auf 1412 angleichen.
ursprünglichen ap1-Plan (der war automatisch L3-Router aller VLANs) ist das beim CRS326 **nicht automatisch gegeben**. - ✅ **Rückweg-Routing — erledigt, keine Zusatzarbeit.** `crs326-core` ist der L3-Router **aller** VLANs
- Firewall: abhängig vom dann geltenden Firewall-Stand (TP5) — Accept `100.96.0.0/12 ↔ 10.40.0.0/16` ggf. gezielt ergänzen. (`vlan10/20/30/40/50/90/99` mit `.1`), Rückverkehr nach `10.40.0.0/16` läuft also ohne Zusatzroute. Das war
- **Governance:** „Config nur via Nautobot" (TP21) → diese CRS326-Zeilen laufen **regulär über Nautobot GC**, sobald beim ursprünglichen CRS326-Retarget die einzige echte Unbekannte.
das Device dort adoptiert ist — die ap1-Ausnahme gilt hierfür **nicht** (s. `teilprojekte.md` Ist-Stand-Zeile 31). - **Namenskollision vermeiden:** auf dem Gerät existiert bereits `wg-hub` (TP10). Das neue Interface heißt
`cf-mesh` — ⚠️ **Bindestrich in `name=` hat RouterOS beim GC-Push schon einmal abgelehnt** (Memory
`ref_gc_push_gotchas_routeros`); beim Direkt-Write über MikroMCP war das nie ein Problem, aber falls die Zeilen
später doch durch die GC laufen, ist `cfmesh` der sichere Name.
- **Firewall:** die `forward`-Kette endet derzeit **ohne Drop** (TP5 steht aus) → der Mesh-Verkehr passiert ohne
Zutun. ⚠️ **Für TP5 vormerken:** sobald deny-by-default greift, braucht es explizit
`forward accept 100.96.0.0/12 ↔ 10.40.0.0/16`, sonst kappt die Firewall den privaten Zugang. Gehört in die
TP5-Whitelist, nicht hierher.
- ✅ **Governance — Ausnahme #4 erteilt (User, 2026-08-16).** Die frühere Zeile („läuft regulär über Nautobot GC")
trägt **nicht mehr**: TP22-Regel 1 schließt jede Sektion mit `<<REDACTED>>` generell vom Push aus, und
`/interface wireguard` ist genau so eine Sektion — ein Privkey ist über GC gar nicht deploybar. Diese vier Zeilen
dürfen daher **direkt** (MikroMCP `plan_changes` dryRun → `apply_plan`). **Auflage wie bei Ausnahme #2:** danach
Ist-Stand als intended config zurück nach Nautobot. **Scope:** nur der Mesh-Bring-up auf `crs326-core`, nicht
spätere WG-Änderungen, nicht andere Geräte. Vorher `/system backup` (TP22-Regel 3).
#### Und wie hält man Secrets langfristig aus der Config raus?
Die Ausnahme soll ein **einmaliger Bootstrap** bleiben, kein Dauerzustand. Der Denkfehler, den man vermeiden
muss, ist, einen privaten Schlüssel als *Konfiguration* zu behandeln — er ist **Geräte-Identität**, wie ein
SSH-Hostkey. Die branchenübliche Regel:
> Der private Teil entsteht auf dem Gerät und verlässt es nie. Nur der **öffentliche** Teil geht in die Source of
> Truth — und wird von dort in die Configs der **Gegenstellen** gerendert.
| Was | Klasse | Wo verwaltet |
|---|---|---|
| PrivateKey | Geräte-Identität | nur am Gerät — nie in Git, nie in der intended config, nie in einem ConfigPlan |
| PublicKey | öffentliche Information | Nautobot (Custom Field/Data) → in die Peer-Configs gerendert |
| `listen-port`, `mtu`, `comment`, Peers, `allowed-address`, Routen, Firewall | Konfiguration | Golden Config, ganz normal |
RouterOS unterstützt das direkt: `/interface wireguard add` **erzeugt den Privkey selbst**, `print` gibt den
Pubkey aus. Compliance bleibt trotzdem grün, weil die `ConfigReplace`-Regel aus TP22 Schritt 0 den Privkey im
Backup auf **byte-identisch dieselbe** Maske normalisiert, die das Template emittiert.
⚠️ **Vorbehalt genau hier:** bei Cloudflare Mesh erzeugt **CF** das Schlüsselpaar (§4.2 holt es per `wgcf-connector`).
Wenn der Flow keinen eigenen Pubkey akzeptiert, fällt die On-Device-Erzeugung weg — das Prinzip („nie in Git, nie
in der intended config") hält trotzdem, der Key wird dann einmalig eingespielt und lebt danach nur am Gerät.
**Stufe 2, falls Secrets doch verwaltet werden müssen** (mehr Geräte, Rotation, PSKs): `nautobot-secrets-providers`
nachinstallieren (Vault/AWS SM/Azure KV/1Password; Core kann schon Env-Var + Text-File) und den echten Wert erst
**bei der ConfigPlan-Erzeugung** einsetzen — gespeicherte intended config behält den Platzhalter, der Plan wird
nach dem Deploy gelöscht. Preis: Klartext liegt transient in Postgres, braucht Retention-Disziplin. Bei vier
Geräten und einem Key lohnt das nicht; **das ist ein TP für die Zeit nach TP18/TP20.1.**
*(Nicht verwechseln: `CredentialsNautobotSecrets` + SecretsGroup „RouterOS Nornir" sind schon im Einsatz — das
sind die **Login**-Credentials für Nornir, nicht Secrets *im Config-Inhalt*.)*
**Sofort fälliger Modellierungs-Nachzug, unabhängig vom Mesh:** die WG-Peers `fm` und `dw` stehen heute mit
hartkodierten `public-key="…"`-Werten im Jinja-Template (`nautobot/mikrotik_routeros.j2`). Public Keys sind Daten,
keine Templatezeilen — die gehören nach Nautobot und von dort gerendert.
### 4.4 Subnetz-Route + vnet-Route umziehen ### 4.4 Subnetz-Route + vnet-Route umziehen
- Ziel: die CIDR-Route `10.40.0.0/16` zeigt auf den **neuen Mesh-Node** statt auf `bf-pve`. - Ziel: die CIDR-Route `10.40.0.0/16` zeigt auf den **neuen Mesh-Node** statt auf `bf-pve`.

368
nautobot/gc-configplan.py Normal file
View File

@@ -0,0 +1,368 @@
#!/usr/bin/env python3
"""BF-Netzwerk -> Nautobot Golden Config: manuelle ConfigPlans anlegen und deployen.
WOFUER
------
Bis 2026-08-16 wurden alle 17 ConfigPlans dieses Projekts ad hoc per curl gebaut. Das
hat zweimal wehgetan (s. GOVERNANCE unten), deshalb hier einmal sauber als Skript.
Ein "manual" ConfigPlan ist der einzige Weg, etwas vom Geraet zu ENTFERNEN:
`plan_type=missing` erzeugt ausschliesslich `add`-Zeilen, `intended` waere ein
Voll-Push (verboten, s. Regel 1). Alles, was Drift vom Typ `extra` beseitigt, ist
also zwangslaeufig ein manueller Plan.
GOVERNANCE (docs/teilprojekte.md TP22 -- verbindlich, nicht optional)
--------------------------------------------------------------------
1. Plans NUR feature-/manual-scoped, NIEMALS "full intended". Ein Voll-Push
schriebe woertlich `passphrase="<<REDACTED>>"` auf die APs (= alle WLAN-Clients
raus) bzw. `private-key="<<REDACTED>>"` auf den Core (= WG-Hub tot).
-> Dieses Skript weigert sich, ein config_set mit `<<REDACTED>>` zu senden.
2. Das AP-Template wird NIE zeilenweise auf ein laufendes Geraet gepusht
(setzt zwischendurch vlan-filtering=no -> reisst den Mgmt-Pfad ab).
3. Vor jedem Push ein `/system backup` auf dem Zielgeraet.
-> Dieses Skript prueft das nicht, es kann nur daran erinnern. Machen.
ZWEI TEUER BEZAHLTE LEHREN, DIE HIER FEST EINGEBAUT SIND
--------------------------------------------------------
* Nautobot-REST sortiert Sammelabfragen ALPHABETISCH. Am 2026-08-13 wurden
4 Device-UUIDs positional zugeordnet -> der Deploy ging an ap1 statt an den Core.
Schadlos nur, weil RouterOS die Kommandos ablehnte. -> `resolve_device()` fragt
IMMER genau einen Namen ab und besteht auf genau einem Treffer; nach dem Anlegen
wird `plan.device.display` gegen den Zielnamen assertet.
* Job-Status ist KEIN Wirksamkeitsnachweis. Der Deploy-Job meldet `SUCCESS`, waehrend
der Plan auf `Failed` steht -- und selbst ein `Completed`-Plan beweist nur, dass
Netmiko die Zeilen abgesetzt hat, nicht dass das Geraet sie angenommen hat.
-> Dieses Skript liest am Ende den PLAN-Status und sagt explizit, dass die
Gegenprobe am Geraet noch aussteht.
WEITERE ROUTEROS-FALLEN (Memory `ref_gc_push_gotchas_routeros`)
---------------------------------------------------------------
* Keine `print`-Zeilen in Config Plans -- `:log info` verwenden.
* Selbstprotokollierendes `:do { ... } on-error={ :log info "..." }` statt nackter
Kommandos: ein Netmiko-Fehlschlag hinterlaesst sonst Teil-Zustand (anders als
NAPALM, das atomar zurueckrollt).
* `napalm-ros` ist READ-ONLY. Der Push laeuft nur, weil in den Golden-Config-
Settings ein `custom_dispatcher` auf `NetmikoMikrotikRouteros` zeigt.
BENUTZUNG
---------
export NAUTOBOT_TOKEN=...
python3 nautobot/gc-configplan.py --job drift-cleanup # Dry-Run
python3 nautobot/gc-configplan.py --job drift-cleanup --apply # anlegen+deployen
python3 nautobot/gc-configplan.py --job drift-cleanup --apply --no-deploy
"""
import argparse
import json
import os
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
BASE = "http://10.40.20.30:8080/api"
TOKEN = os.environ.get("NAUTOBOT_TOKEN", "")
# Status-UUIDs fuer nautobot_golden_config.configplan (2026-08-16 aus der API gelesen)
STATUS_NOT_APPROVED = "4d2d08bf-3bf2-4370-8c8b-248dff2618ab"
STATUS_APPROVED = "7869b4b9-542d-4ff7-a846-92a04943e75d"
JOB_DEPLOY_CONFIG_PLANS = "31c3f093-eeec-43fa-ba4a-0aed2f3c67aa"
JOB_GENERATE_CONFIG_PLANS = "f93bfa53-2332-4fa2-a6ab-5921c802fcee"
# Die Reihenfolge ist NICHT beliebig: die Compliance vergleicht die intended config
# gegen das letzte BACKUP, nicht gegen das Live-Geraet. Wer nach einer Geraetaenderung
# direkt "Compliance" laufen laesst, misst den Stand von vorgestern (TP22-Falle).
REFRESH_SEQUENCE = [
("Backup Configurations", "a48b8c7b-ad4b-4909-8344-1b4e758e75a8"),
("Generate Intended Configurations", "afe60af2-bcd1-4166-8976-1a78f49bc228"),
("Perform Configuration Compliance", "ed3bcb19-5c84-4ffd-a7ba-564703213997"),
]
MASK = "<<REDACTED>>"
# --------------------------------------------------------------------------- HTTP
def req(method, path, data=None):
url = path if path.startswith("http") else f"{BASE}{path}"
body = json.dumps(data).encode() if data is not None else None
r = urllib.request.Request(url, data=body, method=method)
r.add_header("Authorization", f"Token {TOKEN}")
r.add_header("Content-Type", "application/json")
r.add_header("Accept", "application/json")
try:
with urllib.request.urlopen(r, timeout=60) as resp:
raw = resp.read()
return json.loads(raw) if raw else {}
except urllib.error.HTTPError as e:
detail = e.read().decode()[:600]
print(f"!! {method} {url}\n payload={json.dumps(data)[:300]}\n -> {e.code} {detail}")
raise
# ------------------------------------------------------------------- Bausteine
def resolve_device(name):
"""Genau EIN Geraet per Name. Nie positional aus einer Sammelabfrage."""
q = urllib.parse.urlencode({"name": name})
res = req("GET", f"/dcim/devices/?{q}").get("results", [])
if len(res) != 1:
raise SystemExit(f"FEHLER: '{name}' liefert {len(res)} Treffer, erwartet genau 1")
dev = res[0]
if dev.get("name") != name:
raise SystemExit(f"FEHLER: API lieferte '{dev.get('name')}' statt '{name}'")
return dev["id"]
def guard_config_set(config_set, device_name):
"""Regel 1: nie eine Maske aufs Geraet schreiben."""
if MASK in config_set:
raise SystemExit(
f"ABBRUCH: config_set fuer {device_name} enthaelt {MASK}. "
"Das wuerde das echte Secret auf dem Geraet ueberschreiben (TP22-Regel 1)."
)
for line in config_set.splitlines():
if line.strip().startswith("print") or " print " in f" {line} ":
raise SystemExit(f"ABBRUCH: `print` in Config Plan ({device_name}) -- `:log info` verwenden.")
def create_plans(device_names, config_set, change_control_id, dry):
"""Plaene ueber den Job 'Generate Config Plans' anlegen.
WICHTIG: `POST /plugins/golden-config/config-plan/` ist **405 Method Not Allowed** --
das ViewSet ist read-only. ConfigPlans entstehen ausschliesslich ueber diesen Job.
Bei `plan_type=manual` landet der Inhalt des `commands`-TextVar als `config_set`
auf jedem ausgewaehlten Geraet (ein Plan pro Geraet).
"""
guard_config_set(config_set, ",".join(device_names))
dev_ids = [resolve_device(n) for n in device_names]
if dry:
print(f" [dry-run] Job 'Generate Config Plans' fuer {', '.join(device_names)} ({change_control_id})")
for line in config_set.strip().splitlines():
print(f" | {line}")
return []
run = req(
"POST",
f"/extras/jobs/{JOB_GENERATE_CONFIG_PLANS}/run/",
{
"data": {
"device": dev_ids,
"plan_type": "manual",
"commands": config_set,
"change_control_id": change_control_id,
}
},
)
jr = (run.get("job_result") or run).get("id")
wait_job(jr, "Generate Config Plans")
# Plaene ueber die change_control_id einsammeln -- nie positional aus einer
# Sammelabfrage zuordnen (Lehre aus TP21.1: REST sortiert alphabetisch).
q = urllib.parse.urlencode({"change_control_id": change_control_id, "depth": 1})
found = req("GET", f"/plugins/golden-config/config-plan/?{q}").get("results", [])
by_name = {}
for p in found:
name = (p.get("device") or {}).get("display")
by_name[name] = p["id"]
plans = []
for name in device_names:
pid = by_name.get(name)
if not pid:
raise SystemExit(f"ABBRUCH: kein Plan fuer '{name}' unter ccid '{change_control_id}' gefunden")
# Gegenprobe am Objekt selbst, nicht am Suchergebnis.
check = req("GET", f"/plugins/golden-config/config-plan/{pid}/?depth=1")
got = (check.get("device") or {}).get("display")
if got != name:
raise SystemExit(f"ABBRUCH: Plan {pid} haengt an '{got}', erwartet '{name}'. NICHT deployen.")
if check.get("config_set", "").strip() != config_set.strip():
raise SystemExit(f"ABBRUCH: config_set von Plan {pid} weicht ab -- nicht deployen.")
print(f" Plan {pid} -> '{name}' verifiziert (Device + config_set)")
plans.append((name, pid))
return plans
def wait_job(jr, label):
print(f" Job '{label}' gestartet, JobResult {jr}")
for _ in range(60):
time.sleep(5)
st = req("GET", f"/extras/job-results/{jr}/").get("status")
st = st.get("value") if isinstance(st, dict) else st
if str(st).upper() in ("SUCCESS", "FAILURE", "ERRORED", "REVOKED", "COMPLETED", "FAILED"):
print(f" Job-Status: {st} (!! kein Wirksamkeitsnachweis)")
return st
print(" !! Job-Timeout nach 5 Min -- Status manuell pruefen")
return None
def approve(plan_id):
req("PATCH", f"/plugins/golden-config/config-plan/{plan_id}/", {"status": STATUS_APPROVED})
def deploy(plan_ids):
"""Deploy-Job starten und auf das JobResult warten."""
run = req(
"POST",
f"/extras/jobs/{JOB_DEPLOY_CONFIG_PLANS}/run/",
{"data": {"config_plan": plan_ids}},
)
jr = (run.get("job_result") or run).get("id")
wait_job(jr, "Deploy Config Plans")
return jr
def refresh_compliance():
"""Backup -> Intended -> Compliance, strikt in dieser Reihenfolge, dann Bericht."""
for label, job_id in REFRESH_SEQUENCE:
print(f"\n-- {label}")
run = req("POST", f"/extras/jobs/{job_id}/run/", {"data": {}})
jr = (run.get("job_result") or run).get("id")
wait_job(jr, label)
print("\n== Compliance ==")
res = req("GET", "/plugins/golden-config/config-compliance/?limit=500&depth=1")
rows = res.get("results", [])
bad = [r for r in rows if not r.get("compliance")]
print(f" {len(rows) - len(bad)}/{len(rows)} compliant")
for r in bad:
dev = (r.get("device") or {}).get("display")
rule = r.get("rule")
rule = rule.get("display") if isinstance(rule, dict) else rule
print(f" !! {dev} | {rule}")
if r.get("extra"):
print(f" extra: {r['extra'].strip()[:200]}")
if r.get("missing"):
print(f" missing: {r['missing'].strip()[:200]}")
if not bad:
print(" keine Drifts.")
return bad
def plan_status(plan_id):
p = req("GET", f"/plugins/golden-config/config-plan/{plan_id}/?depth=1")
st = p.get("status")
return st.get("display") if isinstance(st, dict) else st
# ------------------------------------------------------------------------ Jobs
def job_drift_cleanup():
"""TP22-Compliance-Backlog: die 4 verbliebenen `extra`-Drifts entfernen.
ap1/ap2: Werks-`defconf` DNS-Static `router.lan -> 192.168.88.1`. Auf beiden APs
der EINZIGE statische Eintrag; die IP existiert im Netz nicht. Der Core hat sein
eigenes, korrektes `router.lan -> 10.40.99.1` (im Template, compliant).
crs326-branch: Bring-up-Rest `vlan10` + `10.40.10.2`. Reihenfolge Adresse ->
Interface. Beruehrt KEINEN Client-Traffic: `/interface bridge vlan` bleibt
unangetastet (`bridge` steht dort statisch als tagged-Member von VLAN10), es
faellt nur die L3-Praesenz des Geraets in VLAN10 weg. Die Deploy-Session selbst
laeuft ueber vlan99/10.40.99.2, also nicht ueber den abgebauten Pfad.
"""
ap_set = (
':do { /ip dns static remove [find address=192.168.88.1] } '
'on-error={ :log info "drift: ap dns-static remove hat geworfen" }\n'
':log info ("drift: rest-defconf-static=" . '
'[:tostr [:len [/ip dns static find address=192.168.88.1]]])'
)
branch_set = (
':do { /ip address remove [find address="10.40.10.2/24"] } '
'on-error={ :log info "drift: branch address remove hat geworfen" }\n'
':do { /interface vlan remove [find name="vlan10"] } '
'on-error={ :log info "drift: branch vlan10 remove hat geworfen" }\n'
':log info ("drift: branch rest-vlan10=" . '
'[:tostr [:len [/interface vlan find name="vlan10"]]] . " rest-addr=" . '
'[:tostr [:len [/ip address find address~"10.40.10."]]])'
)
# (Geraeteliste, commands, change_control_id) -- ap1/ap2 teilen sich das
# config_set, koennen also in einem Job-Lauf erzeugt werden.
return [
(["ap1", "ap2"], ap_set, "TP22-drift-aps-defconf-dns"),
(["crs326-branch"], branch_set, "TP22-drift-branch-temp-vlan10"),
]
JOBS = {"drift-cleanup": job_drift_cleanup}
# ------------------------------------------------------------------------ main
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--job", choices=sorted(JOBS))
ap.add_argument("--apply", action="store_true", help="schreiben (sonst Dry-Run)")
ap.add_argument("--no-deploy", action="store_true", help="Plaene nur anlegen, nicht deployen")
ap.add_argument(
"--deploy-ids",
nargs="+",
metavar="PLAN_ID",
help="bereits angelegte Plaene gestaffelt deployen (Risiko-Reihenfolge selbst waehlen)",
)
ap.add_argument(
"--refresh-compliance",
action="store_true",
help="Backup -> Generate Intended -> Compliance (in dieser Reihenfolge) + Ergebnisbericht",
)
args = ap.parse_args()
if not TOKEN:
raise SystemExit("NAUTOBOT_TOKEN nicht gesetzt")
# Gestaffelter Deploy bereits angelegter Plaene -- fuer Faelle, in denen man
# das riskantere Geraet erst anfassen will, nachdem das harmlose verifiziert ist.
if args.deploy_ids:
for pid in args.deploy_ids:
p = req("GET", f"/plugins/golden-config/config-plan/{pid}/?depth=1")
print(f" {pid} -> {(p.get('device') or {}).get('display')} [{plan_status(pid)}]")
deploy(args.deploy_ids)
print("\n== Plan-Status (massgeblich, nicht der Job-Status) ==")
for pid in args.deploy_ids:
p = req("GET", f"/plugins/golden-config/config-plan/{pid}/?depth=1")
print(f" {(p.get('device') or {}).get('display'):15s} {plan_status(pid)}")
print("\nJetzt am GERAET gegenpruefen -- 'Completed' beweist nichts.")
return
if args.refresh_compliance:
refresh_compliance()
return
if not args.job:
raise SystemExit("--job, --deploy-ids oder --refresh-compliance angeben")
dry = not args.apply
print(f"== {args.job} == {'DRY-RUN' if dry else 'APPLY'}")
if not dry:
print(" Erinnerung Regel 3: `/system backup` auf allen Zielgeraeten vorhanden?")
plans = []
for device_names, config_set, ccid in JOBS[args.job]():
print(f"\n-- {', '.join(device_names)} ({ccid})")
plans.extend(create_plans(device_names, config_set, ccid, dry))
if dry or not plans:
print("\nDry-Run beendet -- nichts geschrieben." if dry else "\nKeine Plaene angelegt.")
return
for _, pid in plans:
approve(pid)
print(f"\n{len(plans)} Plaene auf 'Approved' gesetzt")
if args.no_deploy:
print("--no-deploy: Deploy uebersprungen.")
return
print("\n== Deploy ==")
deploy([pid for _, pid in plans])
print("\n== Plan-Status (massgeblich, nicht der Job-Status) ==")
for name, pid in plans:
print(f" {name:15s} {plan_status(pid)} ({pid})")
print(
"\nNOCH NICHT FERTIG:\n"
" 1. Am GERAET gegenpruefen (MikroMCP) -- Plan 'Completed' beweist nichts.\n"
" 2. Danach in dieser Reihenfolge: Backup -> Generate Intended -> Compliance.\n"
" Die Compliance vergleicht gegen das letzte BACKUP, nicht gegen das Geraet."
)
if __name__ == "__main__":
main()

31
switch/README.md Normal file
View File

@@ -0,0 +1,31 @@
# 📦 ARCHIV — ALLNET-Switch (`sw1`) Telnet-Runner
> **Dieses Verzeichnis ist Historie. Das Gerät existiert nicht mehr.**
> Der ALLNET-Core-Switch `sw1` wurde am **2026-08-08** durch `crs326-core` (MikroTik CRS326, RouterOS)
> ersetzt und **physisch ausgebaut**; in Nautobot steht er auf *Decommissioned* (TP22 Schritt 5).
> Der Omada-Switch `sw2` ist bereits am **2026-07-23** durch `crs326-branch` ersetzt worden (TP15).
## Warum es trotzdem liegen bleibt
Nachvollziehbarkeit: die `.cli`-Dateien und Klickanleitungen dokumentieren, **wie** die VLAN-Struktur
(10/20/30/40/50/90/99/100) ursprünglich entstanden ist — TP1 ist ohne sie nicht rekonstruierbar.
Projektkonvention ist *fortschreiben statt löschen*.
## Was hier **nicht** mehr gilt
| Datei / Ordner | Status |
|---|---|
| `cli/*.cli` + `cli/run-cli.exp` | ❌ nicht mehr ausführbar — kein Gerät, kein Telnet-Endpunkt |
| `cli/03-trunk-ap-ports.cli` | ❌ war schon vor dem Ausbau **stale** (vom Edge-Cutover 2026-07-01 überholt) |
| `ui-guides/01-trunk-ap-ports.md` | 📜 historisch (ausgeführt) |
| `ui-guides/02-vlan99-mgmt-cutover.md` | ⚪ **nie ausgeführt und gegenstandslos** — der Mgmt-VLAN-Umzug für sw1 entfiel mit dem Gerät (TP19.1) |
| `state-backups/sw1-running-config-*.txt` | ⚠️ 214-Byte-Fehlschläge (`Incorrect User Name!!`, toter `claudecode`-Login) — kein verwertbarer Config-Stand |
| `state-PRE/`, `firmware/`, `recovery/`, `sg8316m*` | 📜 Archiv |
## Wo die Wahrheit heute steht
- **Ist-Stand + TP-Steuerung:** `docs/teilprojekte.md`
- **Config-Weg:** ausschließlich **Nautobot Golden Config** (TP21/TP22) — keine Direkt-Changes an Geräten
- **Interaktiv/Debug:** MikroMCP (`crs326-core`, `crs326-branch`, `ap1`, `ap2`)
*Archiviert 2026-08-16 im Zuge der Doku-Aufräum-Session (`docs/doku-cleanup-session.md`, Punkt D).*