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:
33
CLAUDE.md
33
CLAUDE.md
@@ -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.) |
|
||||
| ~~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-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 |
|
||||
| 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` |
|
||||
| **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` ✅ **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 · **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` |
|
||||
| **🔑 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) |
|
||||
| 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-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` ✓ |
|
||||
| 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** |
|
||||
| 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 |
|
||||
|
||||
---
|
||||
@@ -50,26 +50,29 @@ GW je VLAN = `.1` (crs326-core L3) · Mgmt VLAN99: core `.99.1`, branch `.99.2`,
|
||||
## Tooling
|
||||
- **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]`.
|
||||
- **ALLNET-Switch:** kein MCP — nur Telnet-CLI (IOS-artig, FW 2.2.1). Runner:
|
||||
`cd ~/dev/bf-network-r6/switch && . ./.secrets && expect cli/run-cli.exp cli/<datei>.cli`
|
||||
Klickanleitungen in `switch/ui-guides/` für manuelle Schritte.
|
||||
- **~~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).
|
||||
- **~~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.
|
||||
- **~~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).
|
||||
- **Nautobot Golden Config (= der einzige legitime Config-Weg für Netzgeräte):** `nautobot/gc-configplan.py`
|
||||
(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`.
|
||||
Ablösung durch Komodo (native MCP) geplant.
|
||||
|
||||
---
|
||||
|
||||
## Kritische Gotchas
|
||||
- **ALLNET: nur 1 Telnet-VTY** — stale Sessions blockieren ~15 min. Sauber ausloggen (`exit exit`), nie hammern.
|
||||
- **`save` nicht vergessen** nach ALLNET-Änderungen (sonst nach Reboot weg).
|
||||
- 📜 **~~ALLNET-Gotchas (1 Telnet-VTY, `save` nicht vergessen)~~ — gegenstandslos seit 2026-08-08** (Gerät ausgebaut). Nur noch historisch relevant, s. `switch/README.md`.
|
||||
- **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.
|
||||
- **ALLNET VLAN-DB final (TP1 ✅):** 40=MEDIA, 50=UNTRUSTED; GE1/GE2/GE8 alle als Trunk mit 10/20/30/40/50/90/99/100.
|
||||
- **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.)
|
||||
- 📜 **~~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` → `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).
|
||||
- **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.
|
||||
- **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.)
|
||||
- **✅✅ 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).
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# 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.
|
||||
> 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
|
||||
|
||||
@@ -15,23 +15,53 @@
|
||||
| 90 | GUEST | 10.40.90.0/24 | nur Internet |
|
||||
| 99 | MGMT | 10.40.99.0/24 | Switch/AP/Hypervisor-Mgmt |
|
||||
| 100 | WAN | (Arris-Transfer) | zum Arris-Kabelmodem (Liwest, Bridge-Mode, Public-IP an 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)
|
||||
> ⚠️ **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).
|
||||
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 🟢.
|
||||
**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).
|
||||
**Stand 2026-08-16 — live gegen die Geräte geprüft (MikroMCP), nicht aus der Doku abgeschrieben.**
|
||||
|
||||
- **L3 + WAN-Edge = `crs326-core`** (CRS326-24G-2S+, RouterOS 7.19.5, Technik UG): Routing, DHCP (7 Server,
|
||||
54 Reservierungen), DNS, NAT und der WG-Hub (TP10, Peers `fm`/`dw`) laufen hier. Mgmt-SVI **`10.40.99.1`**
|
||||
(vlan99), Gateway `.1` je VLAN (vlan10/20/30/40/50/90/99 alle `running`). **WAN = `vlan100`** — DHCP-Client
|
||||
hält die öffentliche Liwest-IP **`90.146.33.177/24`** direkt vom Arris (Bridge-Mode) → kein Double-NAT.
|
||||
DNS-Upstream 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."*
|
||||
> 🎯 **Ziel: sauberes System „from scratch", keine Leichen/Altlasten.** Migrations-Session-Endstand = **VLAN1 + altes cudy-WLAN komplett weg** (VLAN1 nur noch Mgmt, `defconf`-Client-DHCP zurückgezogen, cudy-Uplinks aufs Ziel-Client-VLAN getaggt oder cudy abgebaut). **Unterpunkte/Aufräum-Liste bei Migration:** (a) die Interim-Härtung ap2 `wifi1/wifi2 frame-types=admit-only-vlan-tagged` (TP3.1, 2026-07-06) bleibt gültig — verschwindet automatisch mit VLAN1; (b) ~~`st-main rrm/wnm` = reines Roaming-Provisorium~~ → **kein Provisorium mehr (2026-08-13, TP3.3): Steering ist fester Bestandteil des Golden-Config-Templates + ComplianceFeature `wifi-steering`** — es war die Neuprovisionierung, die es stillschweigend verloren hatte; (c) ~~**cudy1 = verbleibender aktiver VLAN1-Leak**~~ → **ERLEDIGT (User-bestätigt + verifiziert 2026-08-14): cudy1 ist offline.** `core ether11` trägt **kein Link** (kein `R`-Flag), Nautobot-Status *Offline* (TP22 Schritt 5). Damit existiert kein aktiver VLAN1-Leak mehr; der Dual-DHCP-Race aus `project_vlan1_dhcp_incident` ist Historie. Der formale Teardown (Gerät ausbauen, Doku/Inventar) bleibt TP19.
|
||||
> ⚠️ **HW-Constraint hAP ax²:** 1 GB RAM, aber nur **128 MB Flash + KEIN USB** → **Container auf dem Router NICHT praktikabel**.
|
||||
> Folge: **native RouterOS-Features bevorzugen** (z.B. Built-in-mDNS statt Container); Container (mDNS-Reflector, BIND9-DNS, Omada-Controller) laufen auf **NAS/pve**, nicht am hAP.
|
||||
> 🔒 **Governance-Entscheid (User, 2026-07-16, ausgeweitet 2026-07-16): Config ausschließlich über Nautobot — keine Direkt-Changes mehr an einzelnen im Netz verstreuten Geräten.** Ursprünglich als ap1/ap2-Config-Freeze bis TP21 formuliert, noch am selben Tag verallgemeinert: **jede** Netz-Gerätekonfiguration (Router, APs, künftig auch die CRS326-Switches) läuft ab jetzt exklusiv über Nautobot Golden Config (TP21), sobald das steht — keine ad-hoc MikroMCP-Writes/UI-Klicks mehr an Einzelgeräten. **TP19.1 war die letzte Ausnahme** (ap1-DNS-Fix + ap2/sw1/sw2-Mgmt-VLAN-Umzug, s.u.) — davon ist nur der risikolose ap1-Teil noch direkt gemacht worden, ap2 wurde bewusst auf „nach Nautobot" vertagt. **Macht Nautobot + alle Prerequisites (TP18→TP20→TP20.1→TP21, jetzt auch Komodo) zum aktuell kritischen Pfad**, vor TP5/TP6. Betrifft nicht Software-Migrationen auf NAS/pve (anderes Substrat) und die Frage, ob sw1/sw2 vor der CRS326-Ablösung noch angefasst werden (offen, s. TP15/TP19.1).
|
||||
> ✅ **Genehmigte Ausnahme (User, 2026-07-17): der zentrale CF-Tunnel-Zugang auf ap1 (TP16/TP16.1).** Alle für den zentralen Cloudflare-Zugang **auf ap1** notwendigen (Vor-)Arbeiten dürfen direkt via MikroMCP/CF-API gemacht werden, ohne auf Nautobot GC zu warten — konkret die nativen WireGuard-Mesh-Zeilen aus TP16.1 (und analoge cloudflared/WG-Anpassungen am ap1-Edge). Begründung: Remote-Erreichbarkeit ist die Voraussetzung, um überhaupt sicher am Netz zu arbeiten (Henne-Ei zu Nautobot). **Scope-Grenze:** Die Ausnahme gilt **nur für ap1**. Sobald der zentrale Zugang später auf die **CRS326** wandert (TP15), gilt die Ausnahme dafür **vorerst NICHT** — CRS326-Config läuft dann regulär über Nautobot GC (oder braucht eine eigene, dann neu zu erteilende Ausnahme). Nachtrag der ap1-Ausnahmezeilen in die GC als Follow-up, sobald Nautobot steht.
|
||||
> ✅ **Genehmigte Ausnahme #4 (User, 2026-08-16): die WireGuard-Zeilen des Cloudflare-Mesh-Bring-ups auf `crs326-core` (TP16.1).** Dürfen direkt via MikroMCP/REST gesetzt werden. **Begründung ist strukturell, nicht bequem:** TP22-Regel 1 schließt jede Sektion mit `<<REDACTED>>` generell vom GC-Push aus, und `/interface wireguard` ist genau so eine — ein privater Schlüssel ist über Golden Config **nicht deploybar**. Damit ist die frühere TP16.1-Zusage „CRS326-Zeilen laufen regulär über GC" gegenstandslos. **Auflage:** vorher `/system backup`, danach Ist-Stand als intended config zurück nach Nautobot. **Scope-Grenze:** nur der Mesh-Bring-up auf `crs326-core` — nicht spätere WG-Änderungen, nicht andere Geräte, und sie verlängert sich nicht automatisch. Langfristige Auflösung = das Secret-Handling-Design in TP16.1 (Privkey ist Geräte-Identität, nicht Config).
|
||||
> ✅ **Genehmigte Ausnahme #2 (User, 2026-07-22): der CRS326-Bring-up (TP15).** Die **Grundkonfiguration** beider CRS326 — Management-IP, Bridge, VLAN-Filtering, Trunks/Access-Ports — darf **direkt** via MikroMCP/Winbox gemacht werden. **Begründung = Henne-Ei:** Golden Config kann nicht auf ein Gerät pushen, das im Werkszustand keine erreichbare IP hat, und die GC-Templates existieren noch nicht (TP21 „offen: Templates/intended config"). **Auflage:** der so entstandene Ist-Stand wird anschließend als **intended config nach Nautobot zurückgeschrieben**, danach übernimmt GC — die Ausnahme deckt das Bootstrapping, nicht den Dauerbetrieb. **Scope-Grenze:** gilt für die beiden CRS326 im Bring-up. Sie hebt die Regel für ap1/ap2 **nicht** auf (deren Neuprovisionierung läuft weiterhin über GC) und verlängert sich nicht automatisch auf spätere CRS326-Änderungen. Damit ist die frühere Formulierung in TP16.1 („CRS326-Config läuft regulär über Nautobot GC oder braucht eine eigene, dann neu zu erteilende Ausnahme") **erfüllt** — dies ist diese Ausnahme.
|
||||
|
||||
---
|
||||
@@ -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.
|
||||
- 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):
|
||||
`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).
|
||||
@@ -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`
|
||||
+ `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.
|
||||
- ✅ **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.
|
||||
@@ -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
|
||||
- ✅ **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.
|
||||
- ⏸️ **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).
|
||||
|
||||
### 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
|
||||
- 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:
|
||||
- **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`.**
|
||||
@@ -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).
|
||||
- **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).
|
||||
- **🔜 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**.
|
||||
- ✅ **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).
|
||||
@@ -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).
|
||||
- 📋 **Session-Handover** für die Cutover-Session: `docs/tp15-core-swap-session-handover.md` (Einstieg + Pre-Window-Checkliste + Kurz-Runbook; Original-Auftrag bleibt `docs/tp15-core-swap-session-handoff-prompt.md`).
|
||||
- 📦 **2× CRS326-24G-2S+RM werden morgen (2026-07-17) geliefert, Produktivsetzung spätestens Montag 2026-07-20 (User-Entscheid 2026-07-16).** Sequenzierung ggü. Nautobot geklärt: **TP20+TP21 werden bis dahin fast-getrackt** (TP18/Komodo als Blocker entfernt, s. TP20) — Ziel ist, dass Nautobot **vor** der CRS326-Produktivsetzung steht und die Switches **direkt als Nautobot-verwaltete Geräte adoptiert** werden, statt erst von Hand konfiguriert und später importiert. sw1/sw2-VLAN99-Umzug (TP19.1) läuft parallel dazu on-demand, unabhängig davon.
|
||||
- **🧭 Grundsatzentscheid (2026-07-14, korrigiert 2026-07-15): reines MikroTik-Ecosystem — Routing + Switching + WLAN.** **Omada bekommt KEINE Rolle** (weder Switching noch WLAN — der ursprüngliche „Omada=WLAN"-Satz war ein Fehlschluss beim Dokumentieren, User-Korrektur 2026-07-15: „WLAN war NIE von Omada die Rede"). Begründung Switching: Projektziel #1 **CAKE** nur RouterOS/OpenWrt/OPNsense (Omada-Gateway kann kein CAKE) → Switching = MikroTik CRS. WLAN bleibt auf hAP ax²/CAPsMAN (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.
|
||||
- ✅ **CRS = RouterOS-fähig → in-model:** MikroMCP-Write + Nautobot Golden Config (TP21) managen auch die **Switches**, nicht nur den Router → config-as-code deckt den **ganzen Kabel-Fabric**. (SwOS-Boot bliebe als Fallback möglich, aber nicht genutzt.)
|
||||
- ⚠️ **kein PoE** (Sonoff/PoE-Verbraucher via Injektor, falls nötig).
|
||||
- **📶 WLAN bleibt hAP ax²/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.
|
||||
- **🔄 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".**
|
||||
@@ -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`).
|
||||
- ⚠️ **Aber: passives PoE 18–28 V, NICHT 802.3af/at.** Ein normaler 802.3af/at-Switch oder -Injektor speist den hAP ax² **nicht**. Es braucht einen **passiven PoE-Injektor** (MikroTik-Standard). Dazu passend: **der CRS326 hat gar kein PoE** (bereits dokumentiert) → Strom kommt ohnehin aus einem Inline-Injektor zwischen Switchport und AP, nicht aus dem Switch.
|
||||
- ⚠️ **Config-Konsequenz:** ap1s Trunk liegt heute auf **`ether2`** → wandert bei der Neuprovisionierung auf **`ether1`**. Und `ether1` steht aktuell auf **`poe-out=auto-on`** (der hAP kann über denselben Port auch Strom *abgeben*) — beim Umbau auf „ether1 = versorgter Uplink" muss `poe-out` auf diesem Port **`off`** gesetzt werden, sonst speisen Injektor und AP gegeneinander.
|
||||
- **🔋 DC-USV** (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:**
|
||||
| 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).
|
||||
- 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.**
|
||||
- **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.
|
||||
@@ -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`.
|
||||
- ✅ **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`
|
||||
- ✅ **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).
|
||||
### 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`
|
||||
- ✅ **Gate erfüllt (2026-08-16):** die Bedingung „gated bis TP21 den CRS326 abdeckt" ist eingelöst — `crs326-core` ist seit 2026-08-08 der produktive L3-Router und seit 2026-08-13 in Nautobot adoptiert (68/68 compliant). Runbook §3 neu gemessen, §4.3 von Platzhalter auf **echte Werte** gezogen.
|
||||
- 🧭 **Erwartung geraderücken (wichtig, weil leicht zu verwechseln): das Mesh ersetzt die CF-Tunnel NICHT.** TP16.1 löst ausschließlich den **privaten** Zugang (unterwegs → `10.40.0.0/16` per IP, **inklusive ICMP**) und tauscht dort den cloudflared-QUIC-Proxy gegen natives WireGuard auf dem Core. Die **öffentlichen FQDNs bleiben** auf dem 3-Tunnel-HA aus TP16. Beide Stränge laufen nebeneinander — die HA-Degradierung aus TP16 (2026-08-16) wird davon **nicht** geheilt und bleibt ein eigener offener Punkt.
|
||||
- 📌 **Live-Befunde 2026-08-16 (read-only erhoben, kein Eingriff):**
|
||||
- `warp_connector` = `[]`, aber der **GET antwortet** (kein 403) → die Free-Plan-Frage entscheidet sich erst am `POST` (§4.0). `CF_API_TOKEN` ist `active`; ob er die separate Permission **„Cloudflare One Connector: WARP"** trägt, zeigt genau dieser POST — dort schlug es am 2026-07-17 mit `10000` fehl.
|
||||
- `use_zt_virtual_ip: false` → Unique-Device-IP muss weiterhin eingeschaltet werden. Gateway-Proxy TCP+UDP sind an.
|
||||
- **Nur EIN Device-Profil** (Default). Der „auf beiden Profilen"-Hinweis im Runbook meint also: Default jetzt, Mesh-Profil sobald es existiert — es gibt kein zweites, auf dem man etwas übersehen könnte.
|
||||
- Split-Tunnel-Exclude enthält unverändert `100.64.0.0/10` (verschluckt die Mesh-Overlay-Range) → der §4.1.4-Schritt bleibt nötig.
|
||||
- Die vnet-Route `10.40.0.0/16` (`5546dcb6…`) zeigt weiter auf `bf-pve` — der Fallback steht. ⚠️ **Aber `bf-pve` fährt derzeit nur einen Connector**, der private Pfad hängt also an einem einzelnen cloudflared-Prozess.
|
||||
- ✅ **Die letzte inhaltliche Unbekannte ist weg:** „Rückweg-Routing TBD" hat sich erledigt, weil der Core L3-Gateway **aller** VLANs ist — es braucht keine Zusatzroute. Und `wg-hub` (TP10) liegt auf **demselben** Gerät, ist also direktes Vorbild statt fernes Muster.
|
||||
- ✅ **GOVERNANCE-AUSNAHME #4 (User, 2026-08-16): die WG-Zeilen für den Mesh-Bring-up auf `crs326-core` dürfen DIREKT gesetzt werden** (MikroMCP `plan_changes` dryRun → `apply_plan`). **Begründung — es ist kein Bequemlichkeits-, sondern ein struktureller Grund:** TP22-Regel 1 schließt jede Sektion, die `<<REDACTED>>` enthält, generell vom GC-Push aus, und `/interface wireguard` ist genau so eine Sektion. Ein privater Schlüssel ist über Golden Config also **gar nicht deploybar** — die frühere Zeile „CRS326-Zeilen laufen regulär über Nautobot GC" trägt nicht mehr. **Auflage (wie Ausnahme #2):** Ist-Stand danach als intended config zurück nach Nautobot; vorher `/system backup` (Regel 3). **Scope-Grenze:** nur der Mesh-Bring-up auf `crs326-core` — nicht spätere WG-Änderungen, nicht andere Geräte.
|
||||
- 🔑 **Damit die Ausnahme ein einmaliger Bootstrap bleibt — Secret-Handling-Design (Runbook §4.3):** ein privater Schlüssel ist **Geräte-Identität**, keine Konfiguration (wie ein SSH-Hostkey). Regel: *der private Teil entsteht am Gerät und verlässt es nie; nur der öffentliche geht in die SoT und wird in die **Peer**-Configs gerendert.* RouterOS kann das nativ (`/interface wireguard add` erzeugt den Privkey selbst). Compliance bleibt grün, weil die `ConfigReplace`-Maske aus TP22 Schritt 0 byte-identisch zu der im Template ist. ⚠️ Vorbehalt: bei CF Mesh erzeugt **CF** das Schlüsselpaar — dann fällt die On-Device-Erzeugung weg, das Prinzip („nie in Git, nie in der intended config") hält trotzdem. **Stufe 2** (`nautobot-secrets-providers` + Vault, Wert erst bei der Plan-Erzeugung einsetzen) lohnt bei 4 Geräten und einem Key nicht — eigener TP nach TP18/TP20.1.
|
||||
- 🧹 **Sofort fälliger Modellierungs-Nachzug, unabhängig vom Mesh:** die WG-Peers `fm`/`dw` stehen mit hartkodierten `public-key="…"` im Jinja-Template. **Public Keys sind Daten, keine Templatezeilen** — gehören nach Nautobot und von dort gerendert.
|
||||
- ⚠️ **Für TP5 vormerken:** sobald deny-by-default greift, braucht der Mesh-Verkehr explizit `forward accept 100.96.0.0/12 ↔ 10.40.0.0/16` — heute passiert er nur, weil die `forward`-Kette ohne Drop endet.
|
||||
- ⏭️ **Was die Scharfschalt-Session macht:** `POST warp_connector` (Plan-Gate) → account-weite Prereqs (Unique Device IP, Split-Tunnel auf beiden Profilen) → `wgcf-connector`-Token → 4 WG-Zeilen auf dem Core → Route `5546dcb6…` umhängen (vnet-Gotcha, Weg A/B) → Ping-Abnahme. **Trifft alle Live-WARP-Geräte mit einem kurzen Reconnect** → Zeitpunkt user-gated.
|
||||
- ✅ **Update (User-Entscheid 2026-07-22):** Ziel-Device für den WG-Mesh-Node ist jetzt der **CRS326 Core-Switch statt ap1** — konsistent mit dem TP15-Endstand (kein RB5009) — ⚠️ **die damalige Klammer „Router bleibt ap1/ap2 rein für L3/WLAN" ist seit dem Core-Cutover 2026-08-08 überholt: L3 liegt auf `crs326-core`, ap1/ap2 sind reine APs.** Der Retarget auf den Core war damit im Ergebnis richtig, nur anders begründet als hier notiert und TP21s "ganzer Kabel-Fabric via RouterOS"-Modell (CRS326 ist ohnehin RouterOS-fähig und wird Nautobot-verwaltet). **Reine Ziel-Doku-Änderung** — das Scharfschalten bleibt weiterhin eine eigene, spätere Session und bleibt gated, bis TP21 den CRS326 als Device abdeckt. **Keine neue Governance-Ausnahme:** die bestehende ap1-Ausnahme (s. Ist-Stand-Zeile 31) gilt explizit **nicht** automatisch für den CRS326 — CRS326-Konfiguration soll regulär über Nautobot GC laufen, sobald TP21 so weit ist. Die CRS326 selbst sind aktuell noch Werkszustand (physisch bereits mit dem aktuellen Core verkabelt, aber die betreffende Uplink-Port bislang ungenutzt/down) — konkrete Management-IP/Interface-Zuordnung für den CRS326 als L3/WG-Endpoint ist noch **nicht** getroffen (TBD nach TP15-Bring-up).
|
||||
- **Trennung:** Public FQDNs bleiben auf cloudflared-3-Tunnel-HA (TP16); **Private Access** (unterwegs → `10.40.0.0/16` per IP inkl. ICMP) wandert vom cloudflared-QUIC-Proxy auf einen **nativen WireGuard-Mesh-Node auf dem CRS326 Core-Switch**.
|
||||
- **Warum CRS326-nativ:** Cloudflare Mesh spricht WireGuard, RouterOS kann natives WireGuard (TP10-Hub auf ap1 *ist* einer, Muster überträgt sich) → Mesh-Node = WG-Peer zu CF-PoP, **kein Container/TUN/LXC/neue HW**. Verlagert die Rolle vom Router (ap1, der weiterhin Public-Edge + WLAN-Anker bleibt) auf den Core-Switch, der als zentraler Kabel-Fabric-Knoten ohnehin alle VLANs sieht. RouterOS-Container-Weg bleibt tot (kein `/dev/net/tun`-Passthrough auf JEDER Router/Switch-HW) — daher weiterhin natives WireGuard, kein Container-Ansatz.
|
||||
- **Verworfen:** WG-Roadwarrior (User will keine Extra-App/Profil je Gerät), alle Helper-Host-ZT-Anbieter (SPOF-Verletzung).
|
||||
@@ -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).
|
||||
- **JBOD-vs-RAID1-Ersparnis:** Strom bloß **€9–22/J** (Christophs Zähler) → *kein* Entscheidungsgrund; der Hebel ist **Capex ~€900** (nicht gekaufte Spiegelplatten) + architektonische Korrektheit. (Reicht nicht für ein BW-Upgrade €120–240/J, taugt aber als Seed für HW-Rückstellungen.)
|
||||
- **Konzeptioneller Bruch (Syno-Nachfolger):** Kein *großes resilientes NAS* mehr nötig — die kollektive 3-Site-Redundanz *ist* die Garantie. **Syno-Rolle splittet:** Hot/SMB/VM-Disks bleiben lokal (Syno **oder** ZFS auf pve, klein/schnell); **Backup/Cold wandert in Garage über BF/FM/DW** → das 9-TB-Volume wird **aufgelöst** statt ersetzt (aus 1 SPOF werden 3 geo-redundante Kopien). Backup-Tool (restic/Kopia) sprechen S3 nativ.
|
||||
- **Impacts auf Bestand (Kurz):** (a) **Storage-Redundanz-Philosophie kippt** — weg von SHR/RAID pro NAS, hin zu 3× JBOD; (b) **Backup-Kette** Hyper-Backup/SMB → S3-Bucket umstellen; (c) **Container-Host-System** (TP14) kriegt Garage als LXC/Container je Site, aber auf **rohen Platten**, nicht auf bestehenden SHR-Volumes; (d) **Upload-BW** wird Durchsatz-/Rebuild-Limit; (e) **HW-Planung** verzahnt mit TP15 (Node-HW, 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).
|
||||
- **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`
|
||||
- ⚠️ **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.
|
||||
### 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`
|
||||
- ✅ **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).
|
||||
- 🟢 **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).
|
||||
@@ -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`
|
||||
- **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.
|
||||
- ✅ **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).
|
||||
@@ -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.
|
||||
- ✅ **Basisdaten-Ingest durchgeführt (2026-07-22)** — Ist-Zustand, nicht Zielzustand (User-Entscheid: „wir gehen vom status quo aus"). Quelle = ap1 live via MikroMCP. Skript-Muster idempotent (get-or-create + Delta-PATCH).
|
||||
- **Locations:** neuer LocationType `Bereich` unter `Site`; `BF → Technik UG` (crs326-core, crs326-branch, ap2) und `BF → Wohnzimmer Sideboard` (ap1).
|
||||
- **Devices:** ap1 = Rolle *Router* (Ist!), 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).
|
||||
- **ap1-Interfaces (26):** ether1-5, bridge, lo, wg-hub, vlan10-100, wifi1/wifi2, 7 VAPs — inkl. MACs, `ether2` als Trunk (untagged VLAN1 + tagged 10-100), VAPs als Access-Ports auf ihrem VLAN. 9 Gateway-IPs an ihren Interfaces. Platzhalter-Interface `mgmt` auf ap1 gelöscht (existiert real nicht).
|
||||
- **WLAN:** logischer Controller `bf-wlan` (**kein** CAPsMAN — `controller_device` bewusst leer, die Control-Plane *ist* Nautobot GC) + Gruppe `BF-WLAN-APs` (ap1+ap2) + **6 WirelessNetworks mit VLAN-Bindung** + 2 RadioProfiles (2,4 GHz ch1 / 5 GHz ch36, Domain AT).
|
||||
- 🐞 **API-Gotcha:** `tagged_vlans` wird vom REST-Serializer in Detail-/List-Views als `null` zurückgegeben, **obwohl korrekt gespeichert** — Verifikation nur über **GraphQL** verlässlich. Kostet sonst eine Stunde Fehlersuche an einem Nicht-Problem. Ebenso: Relationen kommen per Default ohne `display` (`?depth=1` nötig), und `VLANGroup`/`Prefix`/`VLAN` brauchen passende `content_types` am LocationType, sonst 400.
|
||||
- ✅ **CRS326-Core L3-Device-Modell + Ports angelegt (2026-08-05):** `crs326-core` jetzt Rolle *Router*, alle VLAN-SVIs (`vlan10/20/30/40/50/90/99/100`) + Gateway-IPs `.1` (außer vlan100) in Nautobot; Portbelegung via `nautobot/ingest-crs326-ports.py` aktualisiert. `crs326-branch` bleibt *Switch* mit Mgmt-IP `.99.13` (Zielrenumber `.12` im Cutover-Fenster).
|
||||
- ⚠️ **Offen/nachzutragen:** exaktes Modell von sw1 + cudy1 (aktuell `… (Modell TBD)`); Standort von sw1/sw2/cudy1 (hängen an `BF` statt an einem Bereich); ap2s `primary_ip4` zeigt noch auf das tote `10.40.41.2` (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.
|
||||
- ✅ **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.
|
||||
@@ -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`).
|
||||
- ✅ **disabled/invalid-Falle bereinigt (User-Hinweis „keine disabled-Falle?"):** (a) die 2 TP10-WG-Forward-Regeln (`allow from/to WG spokes`) waren `I`=invalid, weil aus dem ap1-Backup mit der **stale Interface-ID `*1D`** übernommen → auf `in/out-interface=wg-hub` (per Name) korrigiert; (b) 2 **inaktive WG-Route-Leichen** (`gateway=""`, aus einem früheren Cutover-Versuch vor Existenz von wg-hub) entfernt, nur die aktiven `gateway=wg-hub` bleiben. **Finaler Sweep sauber:** filter-invalid=0, nat-invalid=0, routes-inactive=0, if-disabled=2 (nur ungenutzte `sfp-sfpplus1/2`, gewollt).
|
||||
- ✅ **Backups:** `core-pre-dhcp-restore-20260808.backup` (vor Eingriff) + `core-post-tp21-recovery-20260808.backup` (nach allen Fixes neu gezogen) auf dem Gerät.
|
||||
- ℹ️ **branch** ist gesund (L2, forwardet), Mgmt `10.40.99.2` (+ temp `10.40.10.2`) — 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):**
|
||||
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).
|
||||
@@ -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.
|
||||
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.
|
||||
- **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.
|
||||
@@ -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.
|
||||
- ✅ **Compliance-neutral verifiziert:** Generate-Intended + Compliance über alle 4 Geräte neu gelaufen → weiterhin **51/56** mit exakt denselben 5 bekannten Drifts. Grund: der core-Zweig des Templates iteriert nur Interfaces mit Präfix `vlan` und hält die Bridge-VLAN-Tabelle als Jinja-Konstante. **Umkehrschluss als Dauerregel: die `vlanXX`-Descriptions auf dem Core sind NICHT kosmetisch** — sie landen als `comment=` in der intended config.
|
||||
- 🔍 **Nebenbefund, halbkonfigurierte Ports (NICHT angefasst, gehört zu TP19/TP5):** `core ether11` (cudy1) hat `pvid=10` ohne VLAN10-Membership → funktionslos; `core ether3` hat `pvid=1` bei untagged-VLAN40-Membership; `core ether2` ist gleichzeitig statisches untagged-Member von VLAN1 **und** VLAN40; `branch ether9–23` tragen `pvid=20` ohne jede Membership. Alles Bring-up-Reste, betriebsneutral (kein Link), aber sie gehören in die VLAN1-Aufräumrunde.
|
||||
- ✅ **Nachtrag 2026-08-13 — Compliance-Abdeckung erweitert auf 68 Checks (63/68 grün).** 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.
|
||||
- **`bridge-port`** + **`bridge-vlan`** (`/interface bridge port` / `/interface bridge vlan`) — der **L2-Layer war bis dahin komplett unüberwacht**: der core-Zweig emittierte gar keine Bridge-Sektion, der AP-Zweig nur die VLAN-Tabelle ohne Ports. Jetzt für alle drei Geräteklassen im Template.
|
||||
- 🔑 **Bewusst als Literal-Block, nicht aus DCIM abgeleitet.** Der As-built trägt akzeptierte Schiefstände (`core ether3` ist untagged-Member von VLAN40, hat aber `pvid=1`; `core ether2` hängt zusätzlich in VLAN1; `branch ether9–23` haben `pvid=20` ohne Membership). Würde man `pvid` aus `untagged_vlan` generieren, meldete die Compliance genau diese bekannten Altlasten als Dauerdrift. Der Literal-Block liefert das, was hier gebraucht wird: **Drift-Erkennung gegen den Ist-Zustand**.
|
||||
- ⚠️ **`/interface bridge` selbst bleibt außen vor** — trägt die gerätespezifische `admin-mac`, wäre pro Gerät verschieden und ist kein Fleet-Standard.
|
||||
- 🐞 **Falle dabei: Compliance vergleicht gegen das letzte BACKUP, nicht gegen das Live-Gerät.** Nach dem Steering-Deploy meldete sie erst Drift auf `wifi-configuration`/`wifi-steering`, weil das Backup vom Nachmittag stammte. **Reihenfolge nach jeder Geräteänderung: Backup → Intended → Compliance.**
|
||||
- 📋 **Compliance-Backlog (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`):**
|
||||
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.
|
||||
@@ -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.
|
||||
- **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`)
|
||||
- **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.
|
||||
- **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.
|
||||
@@ -714,7 +808,49 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
|
||||
---
|
||||
|
||||
## 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`.
|
||||
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).
|
||||
@@ -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.
|
||||
6. **Nachgelagert/Research-getrieben:** TP8 (Access), TP9 (HA), TP11 (Monitoring + CF-Tunnel-Track-B/C), TP12 (IPv6), TP13 (Spaß).
|
||||
7. **Foundation (2026-07-14):** **TP15** (MikroTik-Switching / Omada-WLAN, 2× CRS326 bestellt) ∥ **TP20 (Platform-Postgres, single-site replication-ready)** → **TP21 (Nautobot Golden Config)** ∥ **TP20.1 (Vaultwarden→PG + 2-Site-HA)**. TP20 hängt an **TP18 ①** (Garage-Bucket für pgBackRest).
|
||||
|
||||
</details>
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# 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.**
|
||||
> ⚠️ **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.
|
||||
> **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.
|
||||
> *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.
|
||||
> 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),
|
||||
@@ -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-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`
|
||||
- **Noch KEIN** `warp_connector`-Tunnel im Account (`GET /accounts/…/warp_connector` = []).
|
||||
- Device-Settings: **`use_zt_virtual_ip: false`** (Unique-Device-IP AUS — muss für Mesh AN).
|
||||
- Split-Tunnel-Modus = **Exclude**; Exclude-Liste enthält u.a. **`100.64.0.0/10`** (schluckt die Mesh-Overlay-Range
|
||||
`100.96.0.0/12` → Mesh-Traffic würde aktuell NICHT durch WARP gehen). Enthält NICHT `10.0.0.0/8` → deshalb geht
|
||||
`10.40.0.0/16` schon heute über WARP (= warum TCP funktioniert).
|
||||
- vnet-Route `10.40.0.0/16` (vnet `1480b545…`, Route-ID **`5546dcb6-5901-4da6-8d91-b17f94703f71`**) zeigt aktuell
|
||||
auf **cfd_tunnel `bf-pve`** (`f7fdf3a6…`). Das bleibt der **Fallback**, bis Mesh bewiesen ist — nichts vorher löschen.
|
||||
- ap1-WireGuard IST-Stand (bleibt unverändert, unabhängig vom Retarget): 1 Interface `wg-hub` (listen 51821, MTU 1412),
|
||||
Peers `fm` (10.101.0.0/24) + `dw` (10.95.10.0/24) — das ist TP10, nicht Teil dieses TPs.
|
||||
- **CRS326 IST-Stand (Retarget-Ziel):** Werkszustand, noch keine WireGuard-Config, keine Management-IP zugewiesen —
|
||||
das neue Mesh-Interface (outbound zum CF-PoP, kein Listen-Port) entsteht dort auf einem sauberen Blatt, sobald
|
||||
der CRS326-Bring-up (TP15: Management-IP, VLAN-Filtering) steht.
|
||||
- **Noch KEIN** `warp_connector`-Tunnel (`GET /accounts/…/warp_connector` = `[]`). Wichtig: der GET **antwortet**
|
||||
(kein 403) — die Plan-Frage entscheidet sich also erst am `POST` (§4.0).
|
||||
- `devices/settings`: **`use_zt_virtual_ip: false`** → Unique-Device-IP weiterhin **AUS**, muss für Mesh AN.
|
||||
`gateway_proxy_enabled: true`, `gateway_udp_proxy_enabled: true` (ICMP-Flag ist Dashboard-only, s. §4.1.5).
|
||||
- **Genau EIN Device-Profil** (das Default-Profil `0197d1b3-d98a-7c24-b70c-a4d89df7583b`, `service_mode_v2: warp`).
|
||||
➡️ Der „auf BEIDEN Profilen"-Hinweis in §4.1.4 meint deshalb: **Default jetzt, Mesh-Profil sobald es in Schritt 3
|
||||
angelegt ist** — es gibt aktuell kein zweites, auf dem man etwas vergessen könnte.
|
||||
- Split-Tunnel-Modus = **Exclude**; die Liste enthält weiterhin **`100.64.0.0/10`** (schluckt die Mesh-Overlay-Range
|
||||
`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).
|
||||
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.
|
||||
- vnet-Route `10.40.0.0/16` (vnet `1480b545…` = `vnetemsenhuber`, **default**, Route-ID
|
||||
**`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)
|
||||
|
||||
@@ -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`
|
||||
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
|
||||
> Retarget auf den CRS326 (2026-07-22) fehlen noch: die Management-IP/Interface-Zuordnung des CRS326 (TP15-Bring-up),
|
||||
> und der Zugriffsweg für Config-Changes ist dann **Nautobot Golden Config (TP21)**, nicht mehr MikroMCP direkt (die
|
||||
> 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:
|
||||
> Der frühere Platzhalter-Vorbehalt („Management-IP/Interface-Zuordnung fehlt") ist erledigt: Zielgerät ist
|
||||
> **`crs326-core`** (Mgmt `10.40.99.1`, MikroMCP-routerId `crs326-core`). Offen bleibt nur, was erst zur Laufzeit
|
||||
> existiert: der PrivateKey und die `100.96.x.y`-Adresse aus §4.2.
|
||||
|
||||
```
|
||||
/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 \
|
||||
public-key="bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=" \
|
||||
endpoint-address=162.159.193.1 endpoint-port=2408 \
|
||||
allowed-address=0.0.0.0/0,::/0 persistent-keepalive=25s
|
||||
/ip address add address=<100.96.x.y>/32 interface=cf-mesh
|
||||
/ip route add dst-address=100.96.0.0/12 gateway=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 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).
|
||||
- **Rückweg-Routing TBD:** ob der CRS326 selbst L3 für alle VLANs braucht oder nur als Mesh-Endpoint mit einer Route zurück
|
||||
zu ap1 (dem weiterhin bestehenden L3-Router) fungiert, ist Teil der TP15-Bring-up-Entscheidung — anders als beim
|
||||
ursprünglichen ap1-Plan (der war automatisch L3-Router aller VLANs) ist das beim CRS326 **nicht automatisch gegeben**.
|
||||
- Firewall: abhängig vom dann geltenden Firewall-Stand (TP5) — Accept `100.96.0.0/12 ↔ 10.40.0.0/16` ggf. gezielt ergänzen.
|
||||
- **Governance:** „Config nur via Nautobot" (TP21) → diese CRS326-Zeilen laufen **regulär über Nautobot GC**, sobald
|
||||
das Device dort adoptiert ist — die ap1-Ausnahme gilt hierfür **nicht** (s. `teilprojekte.md` Ist-Stand-Zeile 31).
|
||||
|
||||
- **Endpoint:** falls die Config `162.159.192.x` liefert → `162.159.193.x` nehmen (Latenz-Tuning laut Guide).
|
||||
- **MTU:** 1420 aus dem Guide. Direkter Vergleichswert steht jetzt **auf demselben Gerät**: `wg-hub` läuft mit
|
||||
**1412**. Bei Path-MTU-Symptomen (TCP baut auf, große Transfers hängen) zuerst auf 1412 angleichen.
|
||||
- ✅ **Rückweg-Routing — erledigt, keine Zusatzarbeit.** `crs326-core` ist der L3-Router **aller** VLANs
|
||||
(`vlan10/20/30/40/50/90/99` mit `.1`), Rückverkehr nach `10.40.0.0/16` läuft also ohne Zusatzroute. Das war
|
||||
beim ursprünglichen CRS326-Retarget die einzige echte Unbekannte.
|
||||
- **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
|
||||
- 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
368
nautobot/gc-configplan.py
Normal 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
31
switch/README.md
Normal 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).*
|
||||
Reference in New Issue
Block a user