TP28: ap3 (cAP ax) live als CAP, ap1 funkstill, Compliance 130/130 ueber 5 Geraete
Der dritte Access-Point laeuft: 10.40.99.13 auf vlan99, Uplink ether1 -> core ether21, reiner CAP unter crs326-core, Kanaele 5180/ax/Ce/I und 2437/ax (ch6) -- am Geraet nachgemessen, nicht aus dem Plan abgeschrieben. Der Kanalplan stammt aus TP25 vom 2026-08-24; mein erster Entwurf hatte ihn neu erfunden und waere bei ch1 statt ch6 gelandet. Lehre: den TP-Body lesen, nicht nur den Quick-Index. ap1 hat bewusst KEINE Provisioning-Regel mehr: es steht bis zu seinem Umzug ans Sideboard direkt neben ap3 und wuerde auf 5180 kollidieren. Geraet bleibt erreichbar, die Objekte ch-*-ap1 / cfg-main-*-ap1 bleiben fuer den Umzug stehen. Bring-up in zwei Phasen ueber ein Patchkabel ap3 ether2 -> core ether22, weil ueber ether1 nichts ging (s.u.). Loop-Schutz: ether21 war waehrend Phase 1 abgeschaltet und wurde im selben Befehl gegen ether22 getauscht. RouterOS 7.20.8 -> 7.24.1 plus RouterBOOT (zwei Reboots, TP27-Kriterium erfuellt). Die anderen vier stehen auf 7.24 -- die Angleichung ist ein eigener Durchgang, der Core-Reboot ist ein Standort-Ausfall. Nautobot: DeviceType "cAP ax", Device ap3 mit 16 Interfaces, primary_ip4, secrets_group, ConfigContext 5g=ch36 2g=ch6 extra_ports=[]. Nebenkorrektur: ap1 stand auf "Wohnzimmer Sideboard", steht physisch aber in Technik UG (User-Bestaetigung). Sechs Funde, alle als Gotcha in CLAUDE.md und im Handoff: - ether1 ist am Werksgeraet WAN: Discovery aus, Input-TCP verworfen, ICMP aber erlaubt. Es pingt, und das liest sich wie "erreichbar". MikroMCP hilft nicht -- Port 80 ist zu. - pvid ist Ingress, tagged/untagged ist Egress. Ein Lease hing ewig auf "offered", weil der ACK getaggt rausging und ein Werksgeraet das nicht versteht. - `cap enabled=yes` reicht nicht: jedes Master-Radio braucht configuration.manager=capsman, sonst bietet der CAP seine Radios gar nicht an -- state=Ok, radio-Tabelle leer. - Ein Geraet ohne secrets_group erzeugt eine LUECKE, keine Fehlermeldung: Backup scheitert still, keine Compliance-Eintraege, Zahl bleibt unauffaellig bei 104/104. Eine gleichbleibende Compliance-Zahl nach dem Hinzufuegen eines Geraets ist ein BEFUND. - CAPsMAN verteilt keine Login-Passwoerter, nur WLAN-PSKs. Der Geraete-Login ist eine lokale DB pro Geraet; das einheitliche Admin-PW ist eine Konvention von Hand. - Firewall-Regelnummern aus `print where` sind Handles, keine Positionen. Beweis ist der Zaehler, nicht die Zahl. Abnahme: 3 CAPs state=Ok, VAP-Satz vollstaendig und running, 20 IOT-Clients auf ap3 mit 0 % Ping-Verlust ueber VLAN30 (bewusst nicht ueber die Registration-Table abgenommen), Compliance 130/130 / 0 Drifts, Core-Temp-Pfad restlos abgeraeumt. Offen: TP25-Messrunde (braucht Rundgang), PW-Rotation TP30, MikroMCP-Neustart. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
42
CLAUDE.md
42
CLAUDE.md
@@ -9,7 +9,8 @@ DNS/DHCP-Ablösung, WLAN via **CAPsMAN auf dem Core** (TP28, 2026-08-17 — der
|
||||
1. **`docs/teilprojekte.md`** (Quick-Index-Block) — kanonischer Stand, Scope, Abhängigkeiten, **offene** TPs. **1 Read reicht.**
|
||||
2. **`docs/handoff-*.md`** (neueste) — wo die letzte Session aufgehört hat.
|
||||
3. Bei Live-Writes oder Inventur: **`docs/cf-tunnel-mapping.md`**, **`docs/teilprojekte-archiv.md`**.
|
||||
4. Inventur-Befund 2026-08-27: [`docs/handoff-20260827-inventur-und-architektur.md`](docs/handoff-20260827-inventur-und-architektur.md) — Master-Struktur, Asymmetrie, Drift.
|
||||
4. ap3-Bring-up 2026-08-30: [`docs/handoff-20260830-ap3.md`](docs/handoff-20260830-ap3.md) — sechs Funde zu Werks-MikroTiks, CAPsMAN und Golden-Config-Lücken.
|
||||
5. Inventur-Befund 2026-08-27: [`docs/handoff-20260827-inventur-und-architektur.md`](docs/handoff-20260827-inventur-und-architektur.md) — Master-Struktur, Asymmetrie, Drift.
|
||||
|
||||
## Was du sofort wissen musst (Inventur 2026-08-27)
|
||||
|
||||
@@ -59,9 +60,9 @@ GW je VLAN = `.1` (crs326-core L3) · Mgmt VLAN99: core `.99.1`, branch `.99.2`,
|
||||
| ~~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) · Bootstrap Rev4 2026-08-08 | RouterOS **7.24** · `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.24** · `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 (**DoH zum CF-Gateway mit `verify-doh-cert=yes`**, TP23 Stufe 1 ✅ 2026-08-18; `servers=1.1.1.1` nur noch Bootstrap) · **NTP-Server für den Standort**/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.24** · `admin`/`~/.secrets` (`ROUTER_AP1_*`) · REST `http://10.40.99.11/rest` · MikroMCP `ap1` ✅ live · **TP28 ✅ 2026-08-17: reiner CAP** (Manager = `crs326-core`, `cap enabled=yes`, `discovery-interfaces=vlan99`) — Uplink `ether1` (PoE-Port, `poe-out=off`) an crs326-core `ether15`; SSIDs/Kanäle kommen vom Manager, ch36/ch1 · MAC D0:EA:11:4D:40:E0 · Config-Referenz `routeros/.secrets/ap1-prov-base.rsc` |
|
||||
| hAP ax² #1 (ap1) | **10.40.99.11** (VLAN99) · Identity `ap1` | RouterOS **7.24** · `admin`/`~/.secrets` (`ROUTER_AP1_*`) · REST `http://10.40.99.11/rest` · MikroMCP `ap1` ✅ live · **TP28 ✅ 2026-08-17: reiner CAP** (Manager = `crs326-core`) · 🔴 **FUNKSTILL seit 2026-08-30**: keine Provisioning-Regel am Manager, weil ap3 daneben steht und denselben Kanal 5180 fährt. Gerät bleibt erreichbar. Beim Umzug ans Sideboard → `5260/Ce` (Band B, DFS) und Uplink auf `core ether3` — Uplink `ether1` (PoE-Port, `poe-out=off`) an crs326-core `ether15`; SSIDs/Kanäle kommen vom Manager, ch36/ch1 · 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.24** · `admin`/`~/.secrets` (`ROUTER_AP2_*`) · REST `http://10.40.99.12/rest` · MikroMCP `ap2` · **TP28 ✅ 2026-08-17: reiner CAP** — Uplink `ether1` (PoE) an crs326-branch `ether24`; ch44/ch11 (TP25: ch149 war unter ETSI leistungsbegrenzt) · MAC D0:EA:11:4D:40:3A · Config `routeros/.secrets/ap2-prov-base.rsc` |
|
||||
| **cAP ax (ap3)** | **10.40.99.13** (VLAN99, Ziel) · Identity `ap3` | ⏳ **Bring-up offen (TP28-Nachtrag, 2026-08-30)** · Modell `cAPGi-5HaxD2HaxD`, Werkszustand · Uplink `ether1` → **core `ether21`** (Trunk seit 2026-08-30 ✅; Gerät hängt und hat Strom — `link-ok`, MAC `D0:EA:11:FF:16:9D` = Label-E01) · Kanäle **`5180/Ce` + `2437`** (TP25-Plan 08-24) · 🔴 **nur 2 Ethernet-Ports** (`ether1` PoE-in, `ether2` PoE-out) — kein `ether3/4/5` wie am hAP ax² · Dateien `routeros/tp28-ap3-bringup.rsc` + `-lifeline-abbau.rsc` |
|
||||
| **cAP ax (ap3)** | **10.40.99.13** (VLAN99) · Identity `ap3` | ✅ **LIVE seit 2026-08-30** (TP28-Nachtrag) · `cAPGi-5HaxD2HaxD`, RouterOS **7.24.1** + RouterBOOT 7.24.1 (Flotte sonst 7.24 — Differenz bewusst, s. TP27) · reiner CAP, Manager `crs326-core` · Uplink `ether1` → **core `ether21`** (Trunk) · Kanäle **`5180/ax/Ce/I`** (5 GHz) + **`2437/ax`** (ch6) nach TP25-Plan · 🔴 **nur 2 Ethernet-Ports** (`ether1` PoE-in, `ether2` PoE-out, **kein** Bridge-Port) → `ap.extra_ports=[]` · `device-mode=basic` (scheduler/fetch/romon **erlaubt**, anders als ap1/ap2) · Dateien `routeros/tp28-ap3-phase{1,2}-*.rsc` + `-service-accounts.rsc` + `-werksreste.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`** |
|
||||
@@ -104,6 +105,41 @@ GW je VLAN = `.1` (crs326-core L3) · Mgmt VLAN99: core `.99.1`, branch `.99.2`,
|
||||
|
||||
## Kritische Gotchas
|
||||
- 📜 **~~ALLNET-Gotchas (1 Telnet-VTY, `save` nicht vergessen)~~ — gegenstandslos seit 2026-08-08** (Gerät ausgebaut). Nur noch historisch relevant, s. `switch/README.md`.
|
||||
- 🔑 **CAPsMAN verteilt KEINE Login-Passwörter — nur WLAN-PSKs.** Zwei verschiedene Dinge heißen
|
||||
„Passwort": die **WPA-Passphrase** (`/interface wifi security`) liegt am Manager und wird an die
|
||||
CAPs verteilt ✅; der **Geräte-Login** (`/user`) ist eine **lokale Benutzer-DB auf jedem Gerät** ❌.
|
||||
Beleg: ap1 trägt `admin`, `ha`, `nautobot` lokal, mit gerätelokalen `last-logged-in`-Zeitstempeln.
|
||||
Das einheitliche Admin-PW ist eine **von Hand angewendete Konvention**, keine Automatik — und
|
||||
logisch kann es nicht anders sein: ein Gerät *wird* erst zum CAP, nachdem sich jemand eingeloggt hat.
|
||||
- 🔴 **Ein neuer CAP braucht `configuration.manager=capsman` auf JEDEM Master-Radio.**
|
||||
`/interface wifi cap set enabled=yes` allein reicht nicht — ohne das Feld bietet der CAP seine
|
||||
Radios dem Manager gar nicht erst an. **Symptom (2026-08-30, ap3):** `remote-cap print` zeigt
|
||||
`state=Ok`, aber `/interface wifi radio print` am Manager listet für dieses Gerät **nichts**, und
|
||||
weil die Provisioning-Regel dann nicht matcht, sucht man den Fehler in der `radio-mac`. Im
|
||||
Werkszustand ist das Feld leer (= `local`).
|
||||
- 🔌 **Werks-MikroTik am Trunk: `ether1` ist WAN, und das sieht aus wie ein Netzproblem.** Beim
|
||||
cAP ax (und den hAPs) steht `ether1` per defconf in der Interface-Liste `WAN`. Folge: Discovery
|
||||
aus (**kein MNDP → in `/ip neighbor` unsichtbar**), Input-TCP verworfen (22/80/8291/8728 **zu**),
|
||||
**ICMP aber erlaubt** — es *pingt*, was sich wie „erreichbar" liest. Kein Registry-Eintrag und
|
||||
kein MikroMCP hilft, denn Port 80 ist zu. **Der Weg rein ist die LAN-Seite** (`ether2` oder das
|
||||
Werks-WLAN); dort `/interface list member remove [find where interface=ether1]` — das ist der Fix.
|
||||
- 🔀 **`pvid` ist INGRESS, `tagged`/`untagged` ist EGRESS.** Ein Port mit `pvid=99`, der in der
|
||||
*tagged*-Liste von VLAN99 steht, nimmt untagged an und antwortet **getaggt**. Symptom
|
||||
(2026-08-30): ein DHCP-Lease hing ewig auf `offered` und ARP auf `failed` — der DISCOVER kam an,
|
||||
der ACK nie. Für ein Werksgerät gehört der Port **untagged** in die VLAN-Zeile.
|
||||
- 🕳️ **Ein neues Gerät ohne `secrets_group` erzeugt eine LÜCKE, keine Fehlermeldung.** Ohne
|
||||
Secrets Group scheitert der Golden-Config-**Backup**-Job still; die *intended* config wird
|
||||
trotzdem sauber gerendert, aber ohne Backup entstehen **keine Compliance-Einträge** — und die
|
||||
Gesamtzahl bleibt unauffällig (bei uns 104/104), weil ein Gerät ohne Ergebnisse auch keine Drift
|
||||
meldet. 🔴 **Merkregel: eine gleichbleibende Compliance-Zahl nach dem Hinzufügen eines Geräts ist
|
||||
ein BEFUND, kein Erfolg.** Erst `130/130` bewies, dass ap3 wirklich geprüft wird. Zweite
|
||||
Voraussetzung: die dedizierten Service-Accounts `nautobot`/`ha` (TP-PWD) existieren auf einem
|
||||
Werksgerät nicht.
|
||||
- 🔢 **Firewall-Regelnummern aus `print where` sind Handles, keine Positionen.** Nach einem
|
||||
`place-before` zeigte `/ip firewall filter print terse where comment~"..."` die Nummer **60**,
|
||||
während `drop` auf **59** stand — die Regel lag trotzdem davor. Beweis ist der **Zähler**: eine
|
||||
Regel hinter dem `drop` kann nicht zählen. Wer nach der Zahl geht statt nach `print stats`,
|
||||
„repariert" eine funktionierende Regel kaputt.
|
||||
- 🔌 **`crs326-core` hat KEIN PoE-out** (`/interface ethernet poe` existiert dort gar nicht,
|
||||
CRS326-24G-2S+). Ein AP an einem Core-Port braucht eigene Stromversorgung. Der **cAP ax** bringt
|
||||
dafür **Netzteil UND Gigabit-PoE-Injector im Karton** mit (Produktseite, 2026-08-30) — die
|
||||
|
||||
82
docs/handoff-20260830-ap3.md
Normal file
82
docs/handoff-20260830-ap3.md
Normal file
@@ -0,0 +1,82 @@
|
||||
# Handoff 2026-08-30 — ap3 (cAP ax) live, TP28 wieder geöffnet und durchgezogen
|
||||
|
||||
## Was jetzt gilt
|
||||
|
||||
**ap3 funkt.** MikroTik cAP ax (`cAPGi-5HaxD2HaxD`), `10.40.99.13/24` auf `vlan99`, Uplink
|
||||
`ether1` → **`crs326-core ether21`**, reiner CAP unter `crs326-core`. Kanäle **`5180/ax/Ce/I`**
|
||||
(5 GHz) und **`2437/ax`** (ch6) — am Gerät nachgemessen, nicht aus dem Plan abgeschrieben.
|
||||
|
||||
**ap1 ist funkstill.** Es steht bis zu seinem Umzug ans Sideboard direkt neben ap3 und würde auf
|
||||
5180 kollidieren. Es hat deshalb **keine Provisioning-Regel** am Manager; das Gerät bleibt
|
||||
erreichbar, die Objekte `ch-*-ap1`/`cfg-main-*-ap1` bleiben für den Umzug stehen.
|
||||
|
||||
**Compliance: 130/130, 0 Drifts über 5 Geräte.**
|
||||
|
||||
## Was noch offen ist
|
||||
|
||||
1. **TP25-Messrunde** — `bash docs/tp25-runde.sh ap3-bringup`, dann `docs/tp25-walk-messung.sh`
|
||||
für den Gehweg Technik UG ↔ OG. Braucht einen Rundgang mit dem Telefon.
|
||||
Leitfrage: **deckt ap3 allein ab, was ap1 abgedeckt hat, oder klafft bis zum Umzug eine Lücke?**
|
||||
2. **Passwort-Rotation (TP30)** — das gemeinsame MikroTik-Admin-PW ist in dieser Session
|
||||
verbrannt (ungefilterte Ausgabe des `mikromcp`-`env`-Blocks aus `~/.claude.json`). ap3 trägt es
|
||||
bereits, hängt also in der Rotation mit drin. Fehlen zusätzlich in `~/.secrets/exports.zsh`:
|
||||
`MCP_MIKROMCP_ROUTER_AP3_PASS`, `MCP_MIKROMCP_CRS326_CORE_PASS`, `MCP_MIKROMCP_CRS326_BRANCH_PASS`.
|
||||
3. **MikroMCP-Neustart** — Registry (jetzt `bf-infra`, mit ap3) wird nur beim Start gelesen.
|
||||
4. **Versions-Differenz** — ap3 läuft auf **7.24.1**, die anderen vier auf **7.24**. Bewusst:
|
||||
7.20.8 (Januar, long-term) wäre ein halbes Jahr Abstand zur `/interface wifi`-Implementierung
|
||||
des Managers gewesen. Die Angleichung der Flotte ist ein eigener TP27-Durchgang — der
|
||||
Core-Reboot ist ein Standort-Ausfall und damit Terminentscheidung.
|
||||
5. **Kabel `ap3 ether2` → `core ether22`** kann raus. Seit Phase 2 ist `ether2` kein Bridge-Port
|
||||
mehr, das Kabel ist inert.
|
||||
6. **Offene Frage aus TP22:** auf den APs deckt **keine ComplianceRule** `/interface wifi` ab.
|
||||
Genau dort sitzt `configuration.manager=capsman` — ein Feld, dessen Fehlen das WLAN eines APs
|
||||
still verhindert (s. unten). Ob dafür eine Regel sinnvoll ist, ist noch nicht entschieden.
|
||||
|
||||
## Die sechs Funde, die Zeit gekostet haben
|
||||
|
||||
1. **`ether1` ist am Werksgerät WAN.** Discovery aus (kein MNDP), Input-TCP verworfen
|
||||
(22/80/8291/8728 zu) — **aber ICMP erlaubt**. Es *pingt*, und das liest sich wie „erreichbar".
|
||||
MikroMCP hilft nicht: es ist ein HTTP-Client auf Port 80, und der ist zu. Gegenprobe, dass der
|
||||
Core nicht schuld war: Regel 44 `MAIN->MGMT` ist `action=accept` **ohne** Port-Einschränkung.
|
||||
2. **`pvid` ist Ingress, `tagged`/`untagged` ist Egress.** Ein DHCP-Lease hing ewig auf `offered`,
|
||||
ARP auf `failed`: `pvid=99` nahm den DISCOVER an, aber der Port stand in der *tagged*-Liste von
|
||||
VLAN99 — der ACK ging getaggt raus, ein Werksgerät versteht das nicht.
|
||||
3. **`cap enabled=yes` reicht nicht.** Jedes Master-Radio braucht zusätzlich
|
||||
`configuration.manager=capsman`, sonst bietet der CAP seine Radios dem Manager gar nicht an:
|
||||
`remote-cap print` zeigt `state=Ok`, `/interface wifi radio print` bleibt leer — und man sucht
|
||||
den Fehler in der `radio-mac`.
|
||||
4. **Ein neues Gerät ohne `secrets_group` erzeugt eine Lücke, keine Fehlermeldung.** Der Backup-Job
|
||||
scheitert still, die *intended* config wird trotzdem gerendert, und ohne Backup entstehen keine
|
||||
Compliance-Einträge. Die Zahl blieb bei **104/104** — unauffällig. 🔴 **Eine gleichbleibende
|
||||
Compliance-Zahl nach dem Hinzufügen eines Geräts ist ein Befund, kein Erfolg.** Zweite
|
||||
Voraussetzung: die Service-Accounts `nautobot`/`ha` gibt es auf einem Werksgerät nicht.
|
||||
5. **CAPsMAN verteilt keine Login-Passwörter.** Zentral sind die **WLAN-PSKs**; der Geräte-Login
|
||||
(`/user`) ist eine lokale DB pro Gerät. Das einheitliche Admin-PW ist eine von Hand angewendete
|
||||
Konvention.
|
||||
6. **Firewall-Regelnummern aus `print where` sind Handles, keine Positionen.** Nach `place-before`
|
||||
zeigte die TEMP-Regel **60** neben `drop` auf **59** — lag aber davor. Beweis ist der Zähler
|
||||
(`print stats`), nicht die Zahl.
|
||||
|
||||
## Zwei Korrekturen an bestehender Doku
|
||||
|
||||
- **Der Kanalplan war schon da.** TP25 hatte ihn am **2026-08-24** für genau dieses Gerät gerechnet
|
||||
(DFS vom User genehmigt). Ich hatte ihn zunächst neu erfunden und wäre bei ch1 statt **ch6**
|
||||
gelandet. Merkregel: den TP-Body lesen, nicht nur den Quick-Index.
|
||||
- **Der Port war `ether21`, nicht `ether22`.** Der Befund lag in den Daten: in der dynamischen
|
||||
`pvid`-VLAN1-Zeile stand `current-untagged=…,ether21`. Gegenprobe ist
|
||||
`/interface bridge host print where interface=etherN` — dort steht die MAC des Gegenübers.
|
||||
- **ap1 stand in Nautobot auf „Wohnzimmer Sideboard"**, physisch aber in **Technik UG**. Der
|
||||
Eintrag war der Ziel-Standort nach dem geplanten Umzug. Korrigiert.
|
||||
|
||||
## Dateien
|
||||
|
||||
| Datei | Zweck |
|
||||
|---|---|
|
||||
| `routeros/tp28-ap3-phase1-erreichbar.rsc` | über die LAN-Seite: `ether1` aus WAN, Mgmt auf `vlan99` |
|
||||
| `routeros/tp28-ap3-phase2-cap.rsc` | über `vlan99`: Werks-WLAN weg, CAP-Modus, `vlan-filtering=yes` |
|
||||
| `routeros/tp28-ap3-service-accounts.rsc` | `nautobot`/`ha` + Gruppen (sonst kein GC-Backup) |
|
||||
| `routeros/tp28-ap3-werksreste.rsc` | die drei Drifts aus dem ersten Compliance-Lauf |
|
||||
| `nautobot/ingest-ap3-device.py` | DeviceType, Device, 16 Interfaces, IP, SecretsGroup |
|
||||
|
||||
Alle vier `.rsc` tragen Platzhalter statt Secrets; die echten Dateien werden zur Laufzeit ins
|
||||
Scratchpad generiert und danach gelöscht.
|
||||
@@ -70,12 +70,15 @@ liest, liest ihn als Erstes: er wird bei jeder Änderung mitgezogen, nicht nur d
|
||||
(an core `ether15`), ap2 **`10.40.99.12`** (an branch `ether24`), beide **reine CAPs** —
|
||||
`cap enabled=yes`, `discovery-interfaces=vlan99`. Die APs halten **keine eigene WLAN-Config** mehr;
|
||||
Kanäle, SSIDs, Security und Steering kommen vom Manager. 802.11r/k/v aktiv, `ft-over-ds=yes`.
|
||||
🆕 **Dritter AP `ap3` (cAP ax) in Arbeit seit 2026-08-30** — **TP28 dafür wieder geöffnet**.
|
||||
Core-seitig fertig: **`ether21` ist Trunk** (10/20/30/40/50/90/99 tagged, Muster `ether15`) und im
|
||||
GC-Template nachgezogen, Compliance weiter **104/104 / 0 Drifts**. ap3 hängt bereits am Kabel und hat Strom (`link-ok`, MAC `D0:EA:11:FF:16:9D` = Label-E01),
|
||||
der Bring-up steht aber noch aus. Ziel-IP **`10.40.99.13`**, Kanäle **`5180/Ce` + `2437`** nach dem
|
||||
TP25-Kanalplan vom 2026-08-24. ⚠️ **ap1s Radios gehen vorher aus** (steht bis zum Umzug neben ap3);
|
||||
ap1 kehrt später auf `5260/Ce` (DFS) zurück, ap2 auf `5300/Ce`.
|
||||
🆕 **Dritter AP `ap3` (cAP ax) ✅ LIVE seit 2026-08-30** (TP28 dafür wieder geöffnet):
|
||||
`10.40.99.13`, Uplink `ether1` → **core `ether21`** (Trunk), reiner CAP, **RouterOS 7.24.1**
|
||||
(die anderen vier auf 7.24 — Angleichung ist ein eigener TP27-Durchgang). Kanäle
|
||||
**`5180/ax/Ce/I`** + **`2437/ax`** (ch6) nach dem TP25-Plan vom 2026-08-24, am Gerät nachgemessen.
|
||||
🔴 **ap1 ist funkstill** — es steht bis zu seinem Umzug ans Sideboard direkt neben ap3 und würde
|
||||
auf 5180 kollidieren; es hat deshalb bewusst **keine Provisioning-Regel** am Manager, bleibt aber
|
||||
erreichbar. Beim Umzug: ap1 → `5260/Ce` (Band B, DFS, **Frequenz-Liste statt Einzelwert**),
|
||||
ap2 → `5300/Ce`, Uplink ap1 → `core ether3`.
|
||||
**Compliance jetzt 130/130 / 0 Drifts über 5 Geräte** (vorher 104/104 über 4).
|
||||
- **SSDP-Relay** (TP7.1, **nachgebessert 08-25**): LXC **108 `mcast-relay`** auf pve, zweibeinig
|
||||
`10.40.10.21` (VLAN10) + `10.40.40.21` (VLAN40) — dafür trägt `core ether13` beide VLANs; mDNS macht
|
||||
weiterhin der Core. **Läuft seit 08-25 ohne `--ssdpUnicastAddr`** — die Flagge verwarf die M-SEARCH
|
||||
@@ -242,7 +245,7 @@ nachmessen und dann entscheiden**. Blocker gibt es keine mehr. TP5 ist in der Na
|
||||
> | TP23 | ✅ | DNS-Policy (Stufe 1+2 ✅) | 2026-08-26 |
|
||||
> | TP24 | 🔜 | DC-USV | — |
|
||||
> | TP25 | ✅ | WLAN-Stabilisierung (Kern erledigt) | 2026-08-24 |
|
||||
> | TP28 | ⏳ | WLAN/CAPsMAN — **wieder geöffnet**: dritter AP `ap3` (cAP ax) | Kern ✅ 2026-08-18 |
|
||||
> | TP28 | ✅ | WLAN/CAPsMAN — dritter AP `ap3` (cAP ax) **live** | 2026-08-30 |
|
||||
> | TP29 | 💡 | Anwesenheitserkennung (Idee) | — |
|
||||
> | TP30 | 🔜 | Credentials (Etappe 1 ✅, Rest gebündelt) | — |
|
||||
> | TP31 | ✅ | Reverse Proxy Caddy (Schritt 1–4 ✅) | — |
|
||||
@@ -1345,7 +1348,7 @@ die Redundanzfrage oben.
|
||||
|
||||
<a id="tp28"></a>
|
||||
|
||||
### TP28 — WLAN über CAPsMAN auf `crs326-core` ▸ ⏳ **WIEDER GEÖFFNET 2026-08-30: dritter AP `ap3`**
|
||||
### TP28 — WLAN über CAPsMAN auf `crs326-core` ▸ ✅ **ap3 LIVE (2026-08-30)** — wieder geöffnet und durchgezogen
|
||||
|
||||
> **Der CAPsMAN-Kern ist und bleibt ✅ KOMPLETT (2026-08-18)** — der Abschnitt darunter ist
|
||||
> unverändert der Archivtext inkl. Post-Mortem. Wieder geöffnet wurde das TP am 2026-08-30,
|
||||
@@ -1503,19 +1506,36 @@ Umzug **nebeneinander**, ein zweiter AP am selben Punkt bringt nichts und stört
|
||||
> Bridge-Host-Tabelle stellen.** `/interface bridge host print where interface=etherN` nennt die
|
||||
> MAC des Gegenübers — hier `D0:EA:11:FF:16:9D`, exakt die `E01` vom ap3-Label.
|
||||
|
||||
##### Stand 2026-08-30 (diese Session)
|
||||
- ✅ Backups core+ap1 (`pre-ap3-20260830.backup`/`.rsc` auf den Geräten), Compliance-Ausgangsstand
|
||||
**104/104, 0 Drifts**.
|
||||
- ✅ **`core ether21` ist Trunk**: tagged in 10/20/30/40/50/90/99, Muster = `ether15` (ap1).
|
||||
`pvid=1`/`frame-types=admit-all` unverändert; das VLAN1-Bein entsteht dynamisch aus dem pvid.
|
||||
- ✅ **GC-Template nachgezogen und gepusht** (Commit `e25d654b`): `ether21` im Core-L2-Block, und
|
||||
der AP-Zweig rendert die Bridge-Ports jetzt aus **`ap.extra_ports`** statt hartkodiert
|
||||
`ether3/4/5`. **Compliance danach wieder 104/104, 0 Drifts** — das beweist, dass ap1/ap2
|
||||
zeichengleich rendern.
|
||||
- ✅ Bring-up-Dateien geschrieben: [`routeros/tp28-ap3-bringup.rsc`](../routeros/tp28-ap3-bringup.rsc)
|
||||
+ [`routeros/tp28-ap3-lifeline-abbau.rsc`](../routeros/tp28-ap3-lifeline-abbau.rsc).
|
||||
- 🔜 **Offen:** physischer Bring-up (Strom + `/import`), ap1-Radios aus, CAPsMAN-Objekte für ap3,
|
||||
Nautobot-Adoption, PW-Rotation, MikroMCP-Registry, Messrunde.
|
||||
##### Stand 2026-08-30 — ✅ DURCH
|
||||
- ✅ **`core ether21` ist Trunk** (10/20/30/40/50/90/99 tagged, Muster `ether15`), GC-Template
|
||||
nachgezogen; AP-Zweig rendert Bridge-Ports aus **`ap.extra_ports`** statt hartkodiert.
|
||||
- ✅ **Zugang über `ap3 ether2` → `core ether22`** (Patchkabel). Über `ether1` war ap3
|
||||
grundsätzlich nicht administrierbar, s. Gotchas unten. Loop-Schutz: `ether21` war während
|
||||
Phase 1 abgeschaltet, danach im selben Befehl getauscht (`ether22` aus, `ether21` an).
|
||||
- ✅ **Bring-up in zwei Phasen**: Phase 1 (`ether1` aus der WAN-Liste, `10.40.99.13/24` auf
|
||||
`vlan99`, Flotten-PW) über das Kabel, Phase 2 (Werks-WLAN weg, CAP-Modus,
|
||||
`slaves-datapath`, `vlan-filtering=yes`) über `vlan99`.
|
||||
- ✅ **RouterOS 7.20.8 → 7.24.1 + RouterBOOT 7.24.1** (zwei Reboots, TP27-Kriterium
|
||||
`current-firmware = upgrade-firmware`). ⚠️ Die anderen vier stehen auf **7.24**; die
|
||||
Angleichung auf 7.24.1 ist ein eigener TP27-Durchgang (Core-Reboot = Standort-Ausfall).
|
||||
- ✅ **ap1 funkstill** (Provisioning-Regeln entfernt + `remote-cap provision`), gegengeprüft
|
||||
**am CAP**: nur noch `wifi1`/`wifi2` mit `running=false`, keine dynamischen VAPs.
|
||||
- ✅ **ap3 funkt**: `cap-wifi5` = **`5180/ax/Ce/I`**, `cap-wifi6` = **`2437/ax`**, voller
|
||||
VAP-Satz (2 Master + 7 Slaves), alle `running=true`.
|
||||
- ✅ **Datapath bewiesen**: 20 IOT-Clients auf ap3, Ping über VLAN30 mit **0 % Loss**.
|
||||
*(Bewusst nicht über die Registration-Table abgenommen — die zeigte im 14-h-Ausfall
|
||||
25 zufriedene Clients. Bei statisch reservierten Shellys erzeugt ein Roam keine neue
|
||||
DHCP-Lease, deshalb ist hier Datenverkehr das belastbare Signal.)*
|
||||
- ✅ **Nautobot**: DeviceType `cAP ax`, Device `ap3` mit 16 Interfaces, `primary_ip4`,
|
||||
`secrets_group`, ConfigContext (`5g=ch36 2g=ch6 extra_ports=[]`).
|
||||
Nebenkorrektur: ap1 stand auf „Wohnzimmer Sideboard", steht aber in **Technik UG**.
|
||||
- ✅ **Compliance 130/130, 0 Drifts über 5 Geräte.** Die drei Werksreste, die der erste
|
||||
Lauf mit ap3 gemeldet hat (defconf-DNS, 11 defconf-Firewallregeln, fehlende
|
||||
`/ip service`-Härtung), sind mit `routeros/tp28-ap3-werksreste.rsc` abgeräumt.
|
||||
- 🔜 **Offen:** TP25-Messrunde (`bash docs/tp25-runde.sh ap3-bringup`) — braucht einen
|
||||
Rundgang. Leitfrage: deckt ap3 allein ab, was ap1 abgedeckt hat?
|
||||
- 🔜 **Offen:** Passwort-Rotation (TP30) — das gemeinsame Admin-PW ist in dieser Session
|
||||
verbrannt; ap3 trägt es bereits mit, hängt also in der Rotation mit drin.
|
||||
|
||||
##### Was am cAP ax anders ist als am hAP ax² — und was daran wehtut
|
||||
- 🔴 **Nur ZWEI Ethernet-Ports** (`ether1` PoE-in, `ether2` PoE-out) — kein `ether3/4/5`. Der
|
||||
|
||||
@@ -97,9 +97,22 @@ DEVICE_CHANNELS = {
|
||||
# 5 GHz mit 40 MHz: ap1 ch36 belegt 5170-5210 (36+40), ap2 ch44 belegt 5210-5250
|
||||
# (44+48) -> benachbart, ueberlappungsfrei, beide mit /I-Indoor-Freigabe und 18 dBm.
|
||||
# ap2 war bis 2026-08-17 auf ch149 (5745, ETSI-Band C) und dort auf 9 dBm gedrosselt.
|
||||
# 2,4 GHz: ch1/ch11 mit 20 MHz -> ueberlappungsfrei, ch6 bleibt fuer die Nachbarschaft frei.
|
||||
# ap3 (cAP ax, seit 2026-08-30) uebernimmt in Technik UG ch36; ap1 steht bis zu seinem
|
||||
# Umzug daneben und ist deshalb FUNKSTILL (keine Provisioning-Regel am Manager) --
|
||||
# seine Kanaele bleiben hier stehen, weil sie beim Umzug wieder gebraucht werden.
|
||||
# 2,4 GHz: mit ap3 auf ch6 ist 1/6/11 erstmals vollstaendig ueberlappungsfrei.
|
||||
"ap1": {"5g": 36, "2g": 1},
|
||||
"ap2": {"5g": 44, "2g": 11},
|
||||
"ap3": {"5g": 36, "2g": 6},
|
||||
}
|
||||
|
||||
# --- Per-Device: Abweichungen vom ap-common-Default -----------------------------------
|
||||
# Nur eintragen, was wirklich abweicht. Nautobot merged Dicts TIEF, Listen ERSETZEND --
|
||||
# eine leere Liste hier loescht den Default, genau das ist beim cAP ax gewollt.
|
||||
DEVICE_OVERRIDES = {
|
||||
# cAP ax hat nur ether1 (PoE-in) + ether2 (PoE-out); ether2 ist bewusst kein
|
||||
# Bridge-Port. Es bleibt also KEIN zusaetzlicher Bridge-Port uebrig.
|
||||
"ap3": {"extra_ports": []},
|
||||
}
|
||||
|
||||
CTX_NAME = "ap-common"
|
||||
@@ -201,14 +214,15 @@ def main():
|
||||
if not d:
|
||||
print(f" !! Device {dev} nicht gefunden")
|
||||
continue
|
||||
want = {"ap": {"channels": ch}}
|
||||
ov = "".join(f" {k}={v}" for k, v in DEVICE_OVERRIDES.get(dev, {}).items())
|
||||
want = {"ap": {"channels": ch, **DEVICE_OVERRIDES.get(dev, {})}}
|
||||
if d.get("local_config_context_data") == want:
|
||||
print(f" = {dev} 5g=ch{ch['5g']} 2g=ch{ch['2g']} (unveraendert)")
|
||||
print(f" = {dev} 5g=ch{ch['5g']} 2g=ch{ch['2g']}{ov} (unveraendert)")
|
||||
elif DRY:
|
||||
print(f" ~ {dev} -> 5g=ch{ch['5g']} 2g=ch{ch['2g']} (dry-run)")
|
||||
print(f" ~ {dev} -> 5g=ch{ch['5g']} 2g=ch{ch['2g']}{ov} (dry-run)")
|
||||
else:
|
||||
req("PATCH", f"/dcim/devices/{d['id']}/", {"local_config_context_data": want})
|
||||
print(f" ~ {dev} -> 5g=ch{ch['5g']} 2g=ch{ch['2g']}")
|
||||
print(f" ~ {dev} -> 5g=ch{ch['5g']} 2g=ch{ch['2g']}{ov}")
|
||||
|
||||
if DRY:
|
||||
print("\nDRY-RUN - nichts geschrieben. Mit --apply ausfuehren.")
|
||||
|
||||
226
nautobot/ingest-ap3-device.py
Normal file
226
nautobot/ingest-ap3-device.py
Normal file
@@ -0,0 +1,226 @@
|
||||
#!/usr/bin/env python3
|
||||
r"""BF-Netzwerk -> Nautobot: ap3 (cAP ax) als Device anlegen. TP28-Nachtrag, 2026-08-30.
|
||||
|
||||
WOZU
|
||||
----
|
||||
ap3 ist der dritte Access Point und laeuft seit dem 2026-08-30 als reiner CAP unter
|
||||
`crs326-core`. Damit Golden Config ihn rendern und pruefen kann, braucht er ein
|
||||
DCIM-Modell: DeviceType, Device, Interfaces, Mgmt-IP.
|
||||
|
||||
MUSTER = ap1 (per GraphQL gelesen, nicht geraten -- `tagged_vlans` liest sich per REST
|
||||
als null, das war schon in `ingest-ap-rebuild.py` eine Falle).
|
||||
|
||||
DELTAS ZU ap1/ap2 (hAP ax2), und beide sind der Grund fuer die ganze Uebung:
|
||||
- cAP ax hat nur ZWEI Ethernet-Ports (ether1 PoE-in, ether2 PoE-out). Kein ether3/4/5.
|
||||
Deshalb `ap.extra_ports = []` in local_config_context_data; das GC-Template rendert
|
||||
die Bridge-Ports seit 2026-08-30 aus diesem Schluessel statt hartkodiert.
|
||||
- ether2 ist bewusst KEIN Bridge-Port (Zielzustand wie ap2).
|
||||
|
||||
NEBENKORREKTUR: ap1 stand in Nautobot auf "BF -> Wohnzimmer Sideboard", steht physisch
|
||||
aber in Technik UG (User-Bestaetigung 2026-08-30). Der Sideboard-Eintrag war der ZIEL-
|
||||
Standort nach dem geplanten Umzug, nicht der Ist-Zustand. Wird hier mitkorrigiert; beim
|
||||
tatsaechlichen Umzug wandert er zurueck auf Sideboard.
|
||||
|
||||
export NAUTOBOT_TOKEN=...
|
||||
python3 nautobot/ingest-ap3-device.py # Dry-Run
|
||||
python3 nautobot/ingest-ap3-device.py --apply # schreiben
|
||||
"""
|
||||
import json, os, sys, urllib.error, urllib.parse, urllib.request
|
||||
|
||||
BASE = "http://10.40.20.30:8080/api"
|
||||
TOKEN = os.environ.get("NAUTOBOT_TOKEN", "")
|
||||
DRY = "--apply" not in sys.argv
|
||||
|
||||
DEV = "ap3"
|
||||
MODEL = "cAP ax" # board-name, wie "hAP ax2" bei ap1/ap2
|
||||
MANUFACTURER = "MikroTik"
|
||||
LOCATION = "BF → Technik UG"
|
||||
ROLE = "Wireless AP"
|
||||
PLATFORM = "RouterOS"
|
||||
MGMT_IP = "10.40.99.13/24"
|
||||
TRUNK_VLANS = [10, 20, 30, 40, 50, 90, 99]
|
||||
|
||||
# Interfaces in Anlegereihenfolge: bridge zuerst (die anderen referenzieren sie).
|
||||
# radio-mac steht in der description, weil die CAPsMAN-Provisionierung am Core genau
|
||||
# daran haengt -- wer das Geraet tauscht, findet hier, was er am Manager nachziehen muss.
|
||||
IFACES = [
|
||||
("bridge", "bridge", None, None, [], None, None, "vlan-filtering=yes; VLAN1 untagged, 10-99 tagged"),
|
||||
("ether1", "1000base-t", "tagged", 1, TRUNK_VLANS, None, None, "Uplink-Trunk (PoE-in) -> crs326-core ether21"),
|
||||
("ether2", "1000base-t", None, None, [], None, None, "PoE-out, frei. KEIN Bridge-Port - cAP ax hat nur 2 Ports"),
|
||||
("lo", "virtual", None, None, [], None, None, "loopback"),
|
||||
("vlan99", "virtual", "tagged", None, [99], None, "bridge", "Mgmt SVI (VLAN99)"),
|
||||
("wifi1", "ieee802.11ax", "access", 10, [], "bridge", None, "5GHz master - dirtyvader-main (radio-mac D0:EA:11:FF:16:9F)"),
|
||||
("wifi2", "ieee802.11ax", "access", 10, [], "bridge", None, "2.4GHz master - dirtyvader-main (radio-mac D0:EA:11:FF:16:A0)"),
|
||||
("w5-infra", "ieee802.11ax", "access", 20, [], "bridge", None, "5G slave - dirtyvader-infra"),
|
||||
("w5-media", "ieee802.11ax", "access", 40, [], "bridge", None, "5G slave - dirtyvader-media"),
|
||||
("w5-untrusted", "ieee802.11ax", "access", 50, [], "bridge", None, "5G slave - dirtyvader-untrusted"),
|
||||
("w5-guest", "ieee802.11ax", "access", 90, [], "bridge", None, "5G slave - dirtyvader-guest"),
|
||||
("w2-infra", "ieee802.11ax", "access", 20, [], "bridge", None, "2.4G slave - dirtyvader-infra"),
|
||||
("w2-iot", "ieee802.11ax", "access", 30, [], "bridge", None, "2.4G slave - dirtyvader-iot (nur 2.4GHz)"),
|
||||
("w2-media", "ieee802.11ax", "access", 40, [], "bridge", None, "2.4G slave - dirtyvader-media"),
|
||||
("w2-untrusted", "ieee802.11ax", "access", 50, [], "bridge", None, "2.4G slave - dirtyvader-untrusted"),
|
||||
("w2-guest", "ieee802.11ax", "access", 90, [], "bridge", None, "2.4G slave - dirtyvader-guest"),
|
||||
]
|
||||
|
||||
|
||||
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=30) as resp:
|
||||
raw = resp.read()
|
||||
return json.loads(raw) if raw else {}
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f"!! {method} {url}\n -> {e.code} {e.read().decode()[:500]}")
|
||||
raise
|
||||
|
||||
|
||||
def find(path, **params):
|
||||
q = "&".join(f"{k}={urllib.parse.quote(str(v))}" for k, v in params.items())
|
||||
res = req("GET", f"{path}?{q}&limit=2")
|
||||
return res["results"][0] if res.get("count") else None
|
||||
|
||||
|
||||
def main():
|
||||
if not TOKEN:
|
||||
sys.exit("!! NAUTOBOT_TOKEN nicht gesetzt")
|
||||
mode = "DRY-RUN" if DRY else "APPLY"
|
||||
print(f"== ap3 -> Nautobot ({mode}) ==\n")
|
||||
|
||||
status_active = find("/extras/statuses", name="Active")
|
||||
role = find("/extras/roles", name=ROLE)
|
||||
plat = find("/dcim/platforms", name=PLATFORM)
|
||||
loc = find("/dcim/locations", name="Technik UG")
|
||||
manu = find("/dcim/manufacturers", name=MANUFACTURER)
|
||||
# 🔴 OHNE SecretsGroup kein Backup. Das Geraet landet trotzdem in der GC-DynamicGroup,
|
||||
# die intended config wird sauber gerendert -- nur der Backup-Job kann sich nicht
|
||||
# anmelden, und ohne Backup entstehen KEINE Compliance-Eintraege. Die Gesamtzahl
|
||||
# bleibt dann unauffaellig (104/104), weil ein Geraet ohne Ergebnisse auch keine
|
||||
# Drift meldet. Aufgeschlagen am 2026-08-30 bei ap3.
|
||||
secrets = find("/extras/secrets-groups", name="RouterOS Nornir")
|
||||
for label, obj in [("Status Active", status_active), (f"Role {ROLE}", role),
|
||||
(f"Platform {PLATFORM}", plat), ("Location Technik UG", loc),
|
||||
(f"Manufacturer {MANUFACTURER}", manu),
|
||||
("SecretsGroup 'RouterOS Nornir'", secrets)]:
|
||||
if not obj:
|
||||
sys.exit(f"!! {label} nicht gefunden")
|
||||
vlans = {v["vid"]: v["id"] for v in req("GET", "/ipam/vlans/?limit=50")["results"]}
|
||||
|
||||
# --- DeviceType ---
|
||||
dt = find("/dcim/device-types", model=MODEL)
|
||||
if dt:
|
||||
print(f" = DeviceType {MODEL!r}")
|
||||
elif DRY:
|
||||
print(f" + DeviceType {MODEL!r} (dry-run)")
|
||||
else:
|
||||
dt = req("POST", "/dcim/device-types/", {"manufacturer": manu["id"], "model": MODEL, "u_height": 0})
|
||||
print(f" + DeviceType {MODEL!r}")
|
||||
|
||||
# --- Device ---
|
||||
dev = find("/dcim/devices", name=DEV)
|
||||
if dev:
|
||||
print(f" = Device {DEV}")
|
||||
elif DRY:
|
||||
print(f" + Device {DEV} (dry-run)")
|
||||
else:
|
||||
dev = req("POST", "/dcim/devices/", {
|
||||
"name": DEV, "device_type": dt["id"], "role": role["id"], "platform": plat["id"],
|
||||
"location": loc["id"], "status": status_active["id"], "secrets_group": secrets["id"],
|
||||
"comments": "cAP ax (cAPGi-5HaxD2HaxD), reiner CAP unter crs326-core. Bring-up 2026-08-30 (TP28-Nachtrag).",
|
||||
})
|
||||
print(f" + Device {DEV}")
|
||||
|
||||
if DRY and not dev:
|
||||
print("\n (Interfaces/IP koennen im Dry-Run nicht geplant werden, solange das Device fehlt)")
|
||||
print("\nDRY-RUN - nichts geschrieben. Mit --apply ausfuehren.")
|
||||
return
|
||||
|
||||
# SecretsGroup auch nachziehen, wenn das Device schon existierte
|
||||
if dev and not DRY:
|
||||
cur = req("GET", f"/dcim/devices/{dev['id']}/")
|
||||
if (cur.get("secrets_group") or {}).get("id") != secrets["id"]:
|
||||
req("PATCH", f"/dcim/devices/{dev['id']}/", {"secrets_group": secrets["id"]})
|
||||
print(" ~ SecretsGroup 'RouterOS Nornir' gesetzt")
|
||||
|
||||
# --- Interfaces ---
|
||||
print(f"\n Interfaces ({len(IFACES)}):")
|
||||
have = {i["name"]: i for i in req("GET", f"/dcim/interfaces/?device={DEV}&limit=100")["results"]}
|
||||
ids = {n: o["id"] for n, o in have.items()}
|
||||
for name, typ, mode_, untag, tagged, bridge, parent, descr in IFACES:
|
||||
payload = {
|
||||
"device": dev["id"], "name": name, "type": typ, "status": status_active["id"],
|
||||
"enabled": True, "description": descr,
|
||||
}
|
||||
if mode_:
|
||||
payload["mode"] = mode_
|
||||
if untag is not None:
|
||||
payload["untagged_vlan"] = vlans[untag]
|
||||
if tagged:
|
||||
payload["tagged_vlans"] = [vlans[v] for v in tagged]
|
||||
if bridge:
|
||||
payload["bridge"] = ids.get(bridge)
|
||||
if parent:
|
||||
payload["parent_interface"] = ids.get(parent)
|
||||
if name in have:
|
||||
print(f" = {name}")
|
||||
continue
|
||||
if DRY:
|
||||
print(f" + {name:<13} type={typ} mode={mode_} untag={untag} tagged={tagged} (dry-run)")
|
||||
continue
|
||||
new = req("POST", "/dcim/interfaces/", payload)
|
||||
ids[name] = new["id"]
|
||||
print(f" + {name:<13} type={typ} mode={mode_} untag={untag} tagged={tagged}")
|
||||
|
||||
# --- Mgmt-IP + primary_ip4 ---
|
||||
print("\n Mgmt-IP:")
|
||||
ip = find("/ipam/ip-addresses", address=MGMT_IP.split("/")[0])
|
||||
if not ip:
|
||||
if DRY:
|
||||
print(f" + {MGMT_IP} (dry-run)")
|
||||
else:
|
||||
ns = find("/ipam/namespaces", name="Global")
|
||||
ip = req("POST", "/ipam/ip-addresses/", {
|
||||
"address": MGMT_IP, "status": status_active["id"], "type": "host",
|
||||
"namespace": ns["id"], "description": "ap3 Mgmt (VLAN99)"})
|
||||
print(f" + {MGMT_IP}")
|
||||
else:
|
||||
print(f" = {MGMT_IP}")
|
||||
|
||||
if not DRY and ip and ids.get("vlan99"):
|
||||
assigned = req("GET", f"/ipam/ip-address-to-interface/?ip_address={ip['id']}&interface={ids['vlan99']}")
|
||||
if assigned.get("count"):
|
||||
print(" = Zuordnung vlan99")
|
||||
else:
|
||||
req("POST", "/ipam/ip-address-to-interface/", {"ip_address": ip["id"], "interface": ids["vlan99"]})
|
||||
print(" + Zuordnung vlan99")
|
||||
cur = req("GET", f"/dcim/devices/{dev['id']}/")
|
||||
if (cur.get("primary_ip4") or {}).get("id") != ip["id"]:
|
||||
req("PATCH", f"/dcim/devices/{dev['id']}/", {"primary_ip4": ip["id"]})
|
||||
print(" ~ primary_ip4 gesetzt")
|
||||
else:
|
||||
print(" = primary_ip4")
|
||||
|
||||
# --- Nebenkorrektur ap1-Standort ---
|
||||
print("\n Nebenkorrektur ap1-Standort:")
|
||||
ap1 = find("/dcim/devices", name="ap1")
|
||||
if ap1:
|
||||
cur = req("GET", f"/dcim/devices/{ap1['id']}/?depth=1")
|
||||
curloc = (cur.get("location") or {}).get("name")
|
||||
if curloc == "Technik UG":
|
||||
print(" = ap1 steht bereits auf Technik UG")
|
||||
elif DRY:
|
||||
print(f" ~ ap1: {curloc!r} -> 'Technik UG' (dry-run)")
|
||||
else:
|
||||
req("PATCH", f"/dcim/devices/{ap1['id']}/", {"location": loc["id"]})
|
||||
print(f" ~ ap1: {curloc!r} -> 'Technik UG'")
|
||||
|
||||
if DRY:
|
||||
print("\nDRY-RUN - nichts geschrieben. Mit --apply ausfuehren.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -184,6 +184,12 @@
|
||||
/interface wifi channel add band=5ghz-ax frequency=5220 name=ch-5g-ap2 width=20/40mhz
|
||||
/interface wifi channel add band=2ghz-ax frequency=2412 name=ch-2g-ap1 width=20mhz
|
||||
/interface wifi channel add band=2ghz-ax frequency=2462 name=ch-2g-ap2 width=20mhz
|
||||
# ap3 (cAP ax, Technik UG) seit 2026-08-30. Kanaele aus dem TP25-Plan vom 2026-08-24:
|
||||
# Band A (5150-5250) ist das einzige non-DFS-Band mit vollen 23 dBm; ap3 bekommt es, weil
|
||||
# seine Flaeche von keinem anderen AP mitgetragen wird. 2437 = ch6 -> mit ap1 (ch1) und
|
||||
# ap2 (ch11) ist 2,4 GHz erstmals vollstaendig ueberlappungsfrei.
|
||||
/interface wifi channel add band=5ghz-ax frequency=5180 name=ch-5g-ap3 width=20/40mhz
|
||||
/interface wifi channel add band=2ghz-ax frequency=2437 name=ch-2g-ap3 width=20mhz
|
||||
/interface wifi datapath add bridge=bridge name=dp-main vlan-id=10
|
||||
/interface wifi datapath add bridge=bridge name=dp-infra vlan-id=20
|
||||
/interface wifi datapath add bridge=bridge name=dp-iot vlan-id=30
|
||||
@@ -208,12 +214,22 @@
|
||||
/interface wifi configuration add channel=ch-5g-ap2 country=Austria datapath=dp-main mode=ap name=cfg-main-5g-ap2 security=sec-main ssid=dirtyvader-main steering=st-main
|
||||
/interface wifi configuration add channel=ch-2g-ap1 country=Austria datapath=dp-main mode=ap name=cfg-main-2g-ap1 security=sec-main ssid=dirtyvader-main steering=st-main
|
||||
/interface wifi configuration add channel=ch-2g-ap2 country=Austria datapath=dp-main mode=ap name=cfg-main-2g-ap2 security=sec-main ssid=dirtyvader-main steering=st-main
|
||||
/interface wifi configuration add channel=ch-5g-ap3 country=Austria datapath=dp-main mode=ap name=cfg-main-5g-ap3 security=sec-main ssid=dirtyvader-main steering=st-main
|
||||
/interface wifi configuration add channel=ch-2g-ap3 country=Austria datapath=dp-main mode=ap name=cfg-main-2g-ap3 security=sec-main ssid=dirtyvader-main steering=st-main
|
||||
/interface wifi configuration add country=Austria datapath=dp-guest mode=ap name=cfg-guest security=sec-guest ssid=dirtyvader-guest steering=st-guest
|
||||
/interface wifi capsman set ca-certificate=auto certificate=auto enabled=yes interfaces=vlan99 upgrade-policy=none
|
||||
/interface wifi provisioning add action=create-dynamic-enabled comment="ap1 5GHz" master-configuration=cfg-main-5g-ap1 radio-mac=D0:EA:11:4D:40:E5 slave-configurations=cfg-media,cfg-untrusted,cfg-guest
|
||||
/interface wifi provisioning add action=create-dynamic-enabled comment="ap1 2.4GHz" master-configuration=cfg-main-2g-ap1 radio-mac=D0:EA:11:4D:40:E6 slave-configurations=cfg-iot,cfg-media,cfg-untrusted,cfg-guest
|
||||
# 🔴 ap1 hat BEWUSST keine Provisioning-Zeile (2026-08-30). ap3 hat in Technik UG seinen
|
||||
# Platz uebernommen und funkt dort auf 5180; ap1 steht bis zu seinem Umzug ans Sideboard
|
||||
# direkt daneben und wuerde auf demselben Kanal kollidieren. Ohne passende Regel legt
|
||||
# CAPsMAN fuer ein Radio gar nichts an -> es bleibt still, das Geraet aber erreichbar.
|
||||
# Die Objekte `ch-5g-ap1`/`ch-2g-ap1` und `cfg-main-*-ap1` bleiben absichtlich stehen:
|
||||
# beim Umzug bekommt ap1 laut TP25 `5260/Ce` (Band B, DFS) -- dann werden nur diese
|
||||
# zwei Zeilen wieder eingesetzt und das Channel-Objekt auf eine Frequenz-LISTE gestellt
|
||||
# (mit nur einem Wert waere eine Radar-Erkennung ein SPOF).
|
||||
/interface wifi provisioning add action=create-dynamic-enabled comment="ap2 5GHz" master-configuration=cfg-main-5g-ap2 radio-mac=D0:EA:11:4D:40:3E slave-configurations=cfg-media,cfg-untrusted,cfg-guest
|
||||
/interface wifi provisioning add action=create-dynamic-enabled comment="ap2 2.4GHz" master-configuration=cfg-main-2g-ap2 radio-mac=D0:EA:11:4D:40:3F slave-configurations=cfg-iot,cfg-media,cfg-untrusted,cfg-guest
|
||||
/interface wifi provisioning add action=create-dynamic-enabled comment="ap3 5GHz" master-configuration=cfg-main-5g-ap3 radio-mac=D0:EA:11:FF:16:9F slave-configurations=cfg-media,cfg-untrusted,cfg-guest
|
||||
/interface wifi provisioning add action=create-dynamic-enabled comment="ap3 2.4GHz" master-configuration=cfg-main-2g-ap3 radio-mac=D0:EA:11:FF:16:A0 slave-configurations=cfg-iot,cfg-media,cfg-untrusted,cfg-guest
|
||||
|
||||
# [6c] WLAN ACCESS-LIST — der Soll-Zustand ist LEER, und das ist eine Aussage.
|
||||
#
|
||||
|
||||
@@ -64,6 +64,15 @@
|
||||
# Das Objekt traegt bewusst KEINE vlan-id: die liefert der Manager pro VAP.
|
||||
/interface wifi datapath add name=dp-caps-slaves bridge=bridge comment="TP28-fix: bridge fuer CAPsMAN-Slave-VAPs"
|
||||
/interface wifi cap set enabled=yes discovery-interfaces=vlan99 slaves-datapath=dp-caps-slaves
|
||||
# 🔴 `cap enabled=yes` REICHT NICHT. Jedes Master-Radio muss zusaetzlich selbst auf
|
||||
# `configuration.manager=capsman` stehen, sonst BIETET der CAP seine Radios dem
|
||||
# Manager gar nicht an. Symptom (2026-08-30 live aufgeschlagen): `remote-cap print`
|
||||
# zeigt `state=Ok`, aber `/interface wifi radio print` am Manager listet fuer dieses
|
||||
# Geraet NICHTS -- und weil die Provisioning-Regel dann nicht matcht, sucht man den
|
||||
# Fehler in der radio-mac statt an dieser Stelle. ap1/ap2 tragen es seit dem
|
||||
# TP28-Cutover, im Werkszustand ist es leer (= local).
|
||||
/interface wifi set [find name=wifi1] configuration.manager=capsman
|
||||
/interface wifi set [find name=wifi2] configuration.manager=capsman
|
||||
|
||||
# --- (6) ZULETZT scharf schalten. Vorher waere der Rest der Datei nicht angekommen. ---
|
||||
/interface bridge set [find name=bridge] vlan-filtering=yes
|
||||
|
||||
36
routeros/tp28-ap3-service-accounts.rsc
Normal file
36
routeros/tp28-ap3-service-accounts.rsc
Normal file
@@ -0,0 +1,36 @@
|
||||
# =====================================================================================
|
||||
# ap3 — dedizierte Service-Accounts nachziehen. TP28-Nachtrag, 2026-08-30.
|
||||
#
|
||||
# BEFUND: nach dem Bring-up rendert Golden Config die intended config fuer ap3 sauber,
|
||||
# aber der BACKUP-Job scheitert -- Nautobot kommt per SSH nicht auf das Geraet. Ursache
|
||||
# ist kein Netz- und kein Firewall-Problem: ap1/ap2/core/branch tragen seit TP-PWD
|
||||
# (2026-08-23) dedizierte Service-Accounts `nautobot` und `ha`, ein Werksgeraet hat nur
|
||||
# `admin`. Ohne den User gibt es kein Backup, ohne Backup keine Compliance-Eintraege --
|
||||
# und die Gesamtzahl bleibt unauffaellig bei 104/104, weil ein Geraet ohne Ergebnisse
|
||||
# eben auch keine Drift meldet. 🔴 Merkregel: eine gleichbleibende Compliance-Zahl nach
|
||||
# dem Hinzufuegen eines Geraets ist ein BEFUND, kein Erfolg.
|
||||
#
|
||||
# Policies 1:1 von ap1 uebernommen (live gelesen, nicht abgeschrieben):
|
||||
# grp-ha = read,test,api,rest-api + alles andere explizit aus
|
||||
# grp-nautobot = ssh,read,write,test,sensitive + alles andere explizit aus
|
||||
# (write, weil Golden Config auch deployen koennen muss)
|
||||
#
|
||||
# `__HA_PW__` / `__NB_PW__` sind PLATZHALTER. Die echte Datei wird zur Laufzeit aus
|
||||
# ~/.secrets/credentials.env (MIKROTIK_HA_PASS / MIKROTIK_NAUTOBOT_PASS) ins Scratchpad
|
||||
# generiert und danach geloescht -- die Werte stehen nie in diesem Repo.
|
||||
# =====================================================================================
|
||||
|
||||
:if ([:len [/user group find where name="grp-ha"]]=0) do={
|
||||
/user group add name=grp-ha policy=read,test,api,rest-api,!local,!telnet,!ssh,!ftp,!reboot,!write,!policy,!winbox,!password,!web,!sniff,!sensitive,!romon
|
||||
}
|
||||
:if ([:len [/user group find where name="grp-nautobot"]]=0) do={
|
||||
/user group add name=grp-nautobot policy=ssh,read,write,test,sensitive,!local,!telnet,!ftp,!reboot,!policy,!winbox,!password,!web,!sniff,!api,!romon,!rest-api
|
||||
}
|
||||
:if ([:len [/user find where name="ha"]]=0) do={
|
||||
/user add name=ha group=grp-ha password="__HA_PW__" comment="TP-PWD 2026-08-23: dedizierter Service-Account"
|
||||
} else={ /user set [find name="ha"] group=grp-ha password="__HA_PW__" }
|
||||
:if ([:len [/user find where name="nautobot"]]=0) do={
|
||||
/user add name=nautobot group=grp-nautobot password="__NB_PW__" comment="TP-PWD 2026-08-23: dedizierter Service-Account"
|
||||
} else={ /user set [find name="nautobot"] group=grp-nautobot password="__NB_PW__" }
|
||||
|
||||
:log info ("tp28-ap3: service-accounts, users=" . [:tostr [:len [/user find]]] . " groups=" . [:tostr [:len [/user group find]]])
|
||||
39
routeros/tp28-ap3-werksreste.rsc
Normal file
39
routeros/tp28-ap3-werksreste.rsc
Normal file
@@ -0,0 +1,39 @@
|
||||
# =====================================================================================
|
||||
# ap3 — die drei Werksreste abraeumen, die die Compliance nach dem Bring-up gemeldet hat.
|
||||
# TP28-Nachtrag, 2026-08-30.
|
||||
#
|
||||
# Befund aus dem ersten Compliance-Lauf MIT ap3 (127/130):
|
||||
# ip-dns extra -> defconf-Eintrag router.lan -> 192.168.88.1 (die alte
|
||||
# Werks-LAN-IP, die es seit Phase 2 gar nicht mehr gibt)
|
||||
# ip-firewall-filter extra -> 11 defconf-Regeln. Ein reiner AP routet nicht, hat also
|
||||
# keine forward-Kette zu schuetzen; der Mgmt-Zugang haengt
|
||||
# an /ip service, nicht an der input-Kette (TP26).
|
||||
# ip-service missing -> die Haertung war nie gesetzt
|
||||
#
|
||||
# 🔴 `[find !dynamic]` waere ein SYNTAXFEHLER -- korrekt ist `[find where !dynamic]`.
|
||||
# Und Syntaxfehler schlagen beim PARSEN zu, `on-error` faengt sie NICHT. Symptom:
|
||||
# kein Fehler im Log, keine Wirkung, Plan meldet trotzdem Erfolg. (TP26, 2026-08-17.)
|
||||
# 🔴 Das Filtern auf `!dynamic` ist zusaetzlich noetig, weil `remove [find]` auch die
|
||||
# dynamische "special dummy rule to show fasttrack counters" erfasst -- RouterOS
|
||||
# verweigert deren Entfernung, das GESAMTE Kommando wirft, und es wird NICHTS entfernt.
|
||||
#
|
||||
# ⚠️ Kein Lockout-Risiko: `available-from=10.40.0.0/16` deckt alle BF-VLANs ab, also
|
||||
# auch den Mgmt-Weg ueber vlan99 und die Nautobot-Quelle 10.40.20.30.
|
||||
# =====================================================================================
|
||||
|
||||
# [1] /ip service haerten (Soll aus dem GC-Template, platform-weit)
|
||||
/ip service set ftp disabled=yes
|
||||
/ip service set ssh available-from=10.40.0.0/16,100.96.0.0/12
|
||||
/ip service set telnet disabled=yes
|
||||
/ip service set www available-from=10.40.0.0/16,100.96.0.0/12
|
||||
/ip service set winbox available-from=10.40.0.0/16,100.96.0.0/12
|
||||
/ip service set api available-from=10.40.0.0/16,100.96.0.0/12
|
||||
/ip service set api-ssl disabled=yes
|
||||
|
||||
# [2] defconf-Firewall abraeumen (TP26-Muster, identisch zu ap1/ap2)
|
||||
:do { /ip firewall filter remove [find where !dynamic] } on-error={ :log info "tp28-ap3: firewall-remove warf" }
|
||||
|
||||
# [3] defconf-DNS-Eintrag auf die tote Werks-IP
|
||||
:do { /ip dns static remove [find where address="192.168.88.1"] } on-error={ :log info "tp28-ap3: dns-remove warf" }
|
||||
|
||||
:log info ("tp28-ap3-werksreste: fw-statisch=" . [:tostr [:len [/ip firewall filter find where !dynamic]]] . " dns-static=" . [:tostr [:len [/ip dns static find]]])
|
||||
Reference in New Issue
Block a user