4 Commits

Author SHA1 Message Date
75bd43d1f7 TP10: DDNS-Updater live + Mesh-NAT im GC-Template konform — TP abschliessbar
Close-Bedingung 1 (DDNS-Updater bf4.emsenhuber.net):
- Global-Scope-Fix (6e2a7cf): /system script environment get statt :global cfDdnsToken
  (RouterOS 7.24 trennt CLI- und Skript-Scope)
- Live auf crs326-core: Script tp10-ddns-bf4, Trigger DHCP-Client client1 (Lease-Event),
  Token in /system script environment (53 Zeichen)
- Positiv-Test bestanden: A-Record auf 1.2.3.4 gesetzt -> Script-Lauf -> 90.146.33.177,
  Comment '2026.10.03-18:08 by crs326-core', Idempotenz-Variable CFWanIP4Cur gesetzt

Close-Bedingung 2 (Mesh-NAT-Regel im taeglichen GC-Lauf konform):
- nautobot_golden_config: ip-firewall-nat = true fuer crs326-core
- Regel in der Intended Config (Z.425), nicht nur im Backup
- Zwei taegliche AllDevicesGoldenConfig-Laeufe nach dem Fix gruen (2026-10-04, 2026-10-05)

Doku: Handoff-Datei entfernt, TP10-Prosa in teilprojekte.md auf abgeschlossen gesetzt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-05 18:54:57 +02:00
315f1e79e7 TP28: WLAN/CAPsMAN abgeschlossen — ap1-ap3 platziert, Kanalplan committed
- Standort final: ap1 Sideboard · ap2 Technik UG · ap3 Technik OG, alle drei CAPs am crs326-core
- Kanalplan committed (ap1 5260/5280, ap2 5300/5320, ap3 5180), Template mikrotik_routeros.j2
- wifi-cap-Rest auf dem Core (cap enabled=yes, Manager ohne eigene Radios) auf enabled=no bereinigt
- Compliance 29/29 über 5 Geräte, 0 Drifts (145/145 im GC-Job)
- Transaktionen: TEST-ether3-trunk-ap1-sideboard-2 + TP25-kanalplan-ap1-live committed in SoT
- Prosa: Volltext nach teilprojekte-archiv.md, Pointer bleibt in teilprojekte.md
- Gitea #18: body aktualisiert + Abschluss-Kommentar

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-10-05 13:16:42 +02:00
6e2a7cfb47 TP10: Global-Scope-Fix für cfDdnsToken im DDNS-Updater (RouterOS 7.24 Skript-Scope vs CLI-Scope)
Problem: :global cfDdnsToken liest im Skript-Kontext 'nothing', obwohl
/system script environment cfDdnsToken gesetzt ist (53 Zeichen, live verifiziert).

Fix: /system script environment get statt :global — scope-unabhängig auf ROS 7.24.
Skript importiert als tp10-ddns-bf4, Trigger: DHCP-Client client1 (Lease-Event),
kein stündlicher Scheduler nötig (Entscheidung 2026-10-03).

Recovery-Lücke dokumentiert: GC-Template rendert kein /system script /
/system scheduler — Recovery von null hätte Updater nicht.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-03 17:38:55 +02:00
5ad71b37c0 TP10: Mesh-DDNS-Updater gebaut, Handoff für Finalisierung
Skript `tp10-ddns-bf4` auf crs326-core angelegt (MIT, Basis bajodel/mikrotik-cloudflare-dns):
- WAN-IP aus DHCP-Lease (client1/vlan100), NICHT von Echo-Diensten
- check-certificate=yes, Public-IPv4-Guard, Idempotenz, Token als globale Variable

CF_DNS_API_TOKEN (eng gescoped: Zone→DNS:Edit nur emsenhuber.net/bf4) liegt
in ~/.secrets/credentials.env und ist als cfDdnsToken auf Core gesetzt.

Offen: Global-Scope-Problem (Variable im Skript nicht sichtbar) — Handlauf 1+2,
Scheduler, Positiv-Test, TP10 #49 schließen stehen aus.
Handoff-Dokumentation in docs/handoff-20261003-tp10-ddns-finalisierung.md.

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-03 17:17:33 +02:00
3 changed files with 345 additions and 229 deletions

View File

@@ -61,7 +61,7 @@ teuerste Eigenschaft der Datei zerstört, um ihr billigstes Problem zu lösen. B
| [TP25](#tp25) | WLAN-Stabilisierung | ✅ FERTIG (2026-09-15) — Platzierung final (ap1 Sideboard · ap2 UG · ap3 OG), Fund E geschlossen | | [TP25](#tp25) | WLAN-Stabilisierung | ✅ FERTIG (2026-09-15) — Platzierung final (ap1 Sideboard · ap2 UG · ap3 OG), Fund E geschlossen |
| [TP26](#tp26) | `/ip firewall filter` unter Drift-Kontrolle | ✅ DURCH (2026-08-17) | | [TP26](#tp26) | `/ip firewall filter` unter Drift-Kontrolle | ✅ DURCH (2026-08-17) |
| [TP27](#tp27) | RouterBOOT-Firmware auf 7.24 nachziehen | ✅ DURCH (2026-08-17/18) | | [TP27](#tp27) | RouterBOOT-Firmware auf 7.24 nachziehen | ✅ DURCH (2026-08-17/18) |
| [TP28](#tp28) | WLAN über CAPsMAN auf `crs326-core` | ↩️ **wieder geöffnet 2026-08-30** (ap3) — Volltext in [teilprojekte.md](teilprojekte.md#tp28) | | [TP28](#tp28) | WLAN über CAPsMAN auf `crs326-core` | ✅ FERTIG (2026-10-04) — ap1 Sideboard · ap2 UG · ap3 OG, Kanalplan committed, Compliance 29/29 |
| [TP-R](#tp-r) | Router-Capabilities-Sweep | FERTIG (docs/research/routeros-capabilities.md) | | [TP-R](#tp-r) | Router-Capabilities-Sweep | FERTIG (docs/research/routeros-capabilities.md) |
| [TP39](#tp39) | LXC-Hygiene pve: Resolver-Altlasten, `networking.service`, DB-Reverse-Lookup | ✅ ABGESCHLOSSEN (2026-09-01) — pve+NAS-Sweep grün | | [TP39](#tp39) | LXC-Hygiene pve: Resolver-Altlasten, `networking.service`, DB-Reverse-Lookup | ✅ ABGESCHLOSSEN (2026-09-01) — pve+NAS-Sweep grün |
| [TP34](#tp34) | Caddyfile via Nautobot/GC (generische Reverse-Proxy-Schnittstelle) | ❌ VERWORFEN (2026-09-08) — Caddyfile bleibt manuell · **Fortsetzung: TP34.2 (2026-09-22)** | | [TP34](#tp34) | Caddyfile via Nautobot/GC (generische Reverse-Proxy-Schnittstelle) | ❌ VERWORFEN (2026-09-08) — Caddyfile bleibt manuell · **Fortsetzung: TP34.2 (2026-09-22)** |
@@ -1277,11 +1277,222 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung".
### TP28 · Gitea #18 — WLAN über CAPsMAN auf `crs326-core` ### TP28 · Gitea #18 — WLAN über CAPsMAN auf `crs326-core`
> Der Volltext steht seit dem 2026-08-30 wieder in > ✅ **Abgeschlossen 2026-10-04.** ap1 Sideboard · ap2 Technik UG · ap3 Technik OG (Platzierung
> [`teilprojekte.md` → TP28](teilprojekte.md#tp28), weil mit **ap3** (cAP ax) ein dritter > final). Kanalplan committed, `wifi-cap`-Rest auf dem Core bereinigt, Compliance 29/29. Der
> Access-Point dazukommt. **Ein archiviertes TP ist ein geschlossenes Ticket — es darf wieder > ap3-Nachtrag unten ist damit erledigt; die zwei 🔜-Reste sind Cross-TP (TP25 ✅ 09-15, TP30).
> geöffnet werden** (Christoph-Direktive 2026-08-30); der Abschnitt wandert zurück, nicht nur
> ein Nachtrag. Hier bleibt dieser Zeiger stehen, damit alte Links nicht ins Leere gehen.
> **Der CAPsMAN-Kern ist und bleibt ✅ KOMPLETT (2026-08-18)** — der Abschnitt darunter ist
> unverändert der Archivtext inkl. Post-Mortem. Wieder geöffnet wurde das TP am 2026-08-30,
> weil mit **ap3** ein dritter CAP dazukommt; das ist dieselbe Baustelle, kein neues TP
> (Christoph-Direktive: *ein archiviertes TP ist ein geschlossenes Ticket und darf wieder
> geöffnet werden*). **Der offene Teil steht am Ende des Abschnitts unter „Nachtrag ap3".**
- 🔴🔴 **POST-MORTEM 2026-08-18 (gemeldet von Christoph: „die -iot-geräte sind offline … connect möglich, aber kein dhcp"):**
Seit dem Cutover hingen **alle Slave-VAPs in gar keiner Bridge**. `/interface wifi cap slaves-datapath`
war auf beiden APs leer — und genau dieses Feld ist das Einzige, was die vom Manager erzeugten
Slave-VAPs lokal in die Bridge haengt. Der Manager schickt `datapath.bridge=bridge` zwar mit,
**der CAP uebernimmt davon nur die `vlan-id`.** Betroffen: `dirtyvader-iot`, `-media`, `-untrusted`,
`-guest`. Nicht betroffen: `dirtyvader-main` — dessen Master-Radios `wifi1`/`wifi2` sind
**statische** Bridge-Ports und liefen deshalb weiter. Genau das machte den Ausfall unsichtbar.
- **Fix (live 2026-08-18 auf ap1 + ap2, seither Soll im Template):**
`/interface wifi datapath add name=dp-caps-slaves bridge=bridge` +
`/interface wifi cap set slaves-datapath=dp-caps-slaves`. Das Objekt traegt **bewusst keine
`vlan-id`** — die liefert der Manager pro VAP. Danach erscheinen die VAPs als **dynamische
Bridge-Ports mit `pvid`=30/40/50/90**, und DHCP laeuft sofort wieder.
- 🔴 **Die Diagnose-Lehre: „Client assoziiert" beweist gar nichts.** Am Manager standen 25 IOT-
Registrations — das WLAN sah perfekt aus. Erst zwei Reads trennten die Faelle:
`torch` auf `vlan30` am Core (**leer**) und der TP5-Drop-Log (**kein einziger DHCP-Eintrag**).
Beides leer heisst: es wird nichts verworfen, es **kommt** nichts. Damit war die Firewall
— der naheliegende Verdaechtige nach der Haertung derselben Nacht — in zwei Minuten entlastet.
- ⚠️ **Die Abnahme „✅ Ohne jeden Aussetzer" weiter unten war falsch.** Sie zaehlte Registrations
(30 Clients, beide CAPs `Ok`, 7 VAPs je AP) — also genau die Metrik, die hier luegt. Keiner
dieser Werte beruehrt die Bridge. Ein Abnahmekriterium fuer WLAN-Umbauten ist ab jetzt eine
**frische DHCP-Lease pro SSID**, nicht die Registration-Table.
- **`routeros/tp28-ap-wifi-reste-abraeumen.rsc` hat den Ausfall nicht ausgeloest, aber zementiert:**
mit den geloeschten datapath-Objekten gab es lokal nichts mehr, worauf `slaves-datapath` haette
zeigen koennen. Der Ein-Zeilen-Fix war damit weg. Nachtrag steht im Skript.
- ✅ **Reboot-Abnahme an ap2 (2026-08-18), drei Fragen auf einmal beantwortet:** `slaves-datapath`
ueberlebt den Neustart (statisches Objekt, steht im `/export`), der CAP **findet seinen Manager
durch die gehaertete input-Kette wieder** (`current-caps-man-identity=crs326-core` — MNDP/CAPWAP
sind also korrekt offen, das war seit TP5 Etappe 4 nie durchgespielt), und die sieben Slave-VAPs
kommen von selbst als dynamische Bridge-Ports mit pvid 30/40/50/90 zurueck. AP nach **40 s**
wieder erreichbar. Abnahme nach neuer Regel = **frische Lease**: IOT-Client `10.40.30.8`
`last-seen=30s`, MAIN-Client `10.40.10.180` `last-seen=1m54s`.
- ⚠️ **Reboot per MikroMCP geht auf 7.24 nicht:** das `reboot`-Tool sendet `delay`, die REST-API
antwortet `unknown parameter delay`; `run_command` ist fuer `/system reboot*` per Deny-Policy
gesperrt. Weg bis das gefixt ist: `sshpass` + `ssh admin@<ap> "/system reboot"`.
- ✅ **Soll nachgezogen, Compliance wieder sauber: 92/92, 0 Drifts** (2026-08-18). Weg: Template in
`nautobot/mikrotik_routeros.j2` → `routeros/push-gc-template.py` (Secret-Guard) → GitRepository-Sync
→ Backup/Intended/Compliance. Ohne den Push meldet der Refresh die zwei neuen Zeilen als `extra`
auf beiden APs — das Jinja lebt im **separaten** Gitea-Repo `bf-golden-config`, ein Commit hier
allein erreicht Nautobot nicht.
- ⚠️ **Beim ersten Sync-Versuch: `redis.exceptions.ConnectionError: broken pipe`** auf dem
Nautobot-Stack. Ein simpler Retry ging durch; NAS-Load war unauffaellig (0,9). Der Job meldet das
als `FAILURE` — nicht mit einem inhaltlichen Fehler verwechseln.
- ✅ **Der offene GC-Rest ist erledigt (2026-08-18).** Die fuenf `wifi-*`-ComplianceFeatures haengen
jetzt am **Core**, der AP-Zweig des Templates ist auf eine einzige Zeile eingedampft
(`/interface wifi cap set discovery-interfaces=vlanXX enabled=yes`).
- 🔴 **Dabei aufgefallen: die APs trugen noch die KOMPLETTE Standalone-WLAN-Config** aus TP3 —
je 2 channel, 6 datapath, 6 security, 7 configuration, 1 steering. Der Cutover vom 08-17 hatte
sie nie abgeraeumt. Wirkungslos (der Manager liefert alles dynamisch), aber sie im Template als
Soll zu rendern hiesse, totes Zeug zur Absicht zu erklaeren — dasselbe Argument wie bei den
defconf-Firewallregeln in TP26. Abgeraeumt mit `routeros/tp28-ap-wifi-reste-abraeumen.rsc`.
- ✅ **Ohne jeden Aussetzer:** vorher/nachher 30 Clients, beide CAPs `Ok`, 7 dynamische VAPs je AP.
Die Sorge vor einem Radio-Re-Apply (TP25/TP3.3: „wirft jeden Client kurz raus") hat sich nicht
bestaetigt — die dynamischen VAPs tragen ihre Werte **inline** (`datapath.vlan-id=…`) und
referenzieren kein lokales Objekt; nur `wifi1`/`wifi2` hielten noch einen toten
`configuration=`-Verweis, der beim Loeschen folgenlos blieb.
- 🔴 **`[find !dynamic]` ist auf diesen Sektionen ein SYNTAXFEHLER**, nicht bloss wirkungslos:
`/interface wifi configuration` & Co. haben gar keine `dynamic`-Eigenschaft (die gibt es nur auf
`/interface wifi` selbst). Der erste Anlauf lief komplett in die `on-error`-Zweige, aenderte
**nichts** und meldete trotzdem „Plan Completed". Wieder: nur die Gegenpruefung am Geraet zaehlt.
- ✅ **Drei neue ComplianceRules schliessen die letzten WLAN-Luecken:** `wifi-capsman`
(der Manager-Schalter), `wifi-provisioning` (die vier Regeln je `radio-mac` — sie entscheiden,
welcher AP welche Kanaele und Slave-VAPs bekommt) und `wifi-cap` (die CAP-Seite; faellt
`discovery-interfaces` weg, findet der AP seinen Manager nach dem naechsten Reboot nicht mehr).
**Ergebnis: 92/92 compliant, 0 Drifts.**
- ⚠️ **`wifi-security` ist gruen, aber NICHT deploybar.** Die Passphrasen stehen als `<<REDACTED>>`
im Template (User-Entscheid 2026-08-18: alle fuenf Features umhaengen, Platzhalter in Kauf nehmen).
Der gruene Haken beweist nur, dass Soll und Ist dieselbe **Maske** tragen — nicht, dass die
Passphrasen stimmen, und schon gar nicht, dass die Sektion ausgerollt werden koennte. Das steht
als Warnung im Template. Wer sie aendert, liest den Ist-Stand per REST zurueck
(`GET http://10.40.99.1/rest/interface/wifi/security`) — **nicht** aus `wlan-psks.env` oder den
drei Bring-up-Dateien, die voneinander abgeschrieben sind.
- ⚠️ **Die frueher hier notierte „bisher fehlende Rule auf `/interface wifi`" ist gegenstandslos** —
wie vorhergesagt: ein CAP hat keine Inline-Overrides mehr, die driften koennten.
> **Methodenwechsel auf Christophs Anstoß:** *„ich möchte die methode in frage stellen … eine sot ist zwar nett,
> aber wenn sie uns das leben zur hölle macht, verzichte ich auf diese governance."* Der Entscheid vom
> 2026-07-22, CAPsMAN abzuschaffen, ist damit **revidiert**.
- ✅ **Warum es diesmal trägt:** 2026-07-22 war **ap1 Manager UND AP** — und ein Manager kann seine eigenen
Radios nicht per CAPsMAN provisionieren (MikroTik-bestätigt). Genau diese Asymmetrie war die Ursache von
TP3.1. Mit dem **Core** als Manager sind beide APs symmetrisch. **Der Core braucht dafür kein `wifi-qcom`:**
das `capsman`-Menü steckt im Basis-`routeros`, die 3,35 MB freier Flash sind irrelevant. Die zweite
Begründung von damals („WLAN instabil") war ohnehin widerlegt — standalone blieb es instabil, die Ursachen
waren die Funkparameter aus TP25.
- ✅ **Ist-Stand:** Manager auf `vlan99`, 4 Kanäle · 6 Datapaths · 6 Security · 2 Steering · 9 Configurations ·
4 Provisioning-Regeln je `radio-mac`. Beide `remote-cap` **`Ok`**, je 7 dynamische VAPs. Kanäle exakt wie in
TP25 gemessen (ap1 `5180/ax/Ce/I`+`2412/ax`, ap2 `5220/ax/Ce/I`+`2462/ax`). **34 Clients** (main 3, iot 27,
media 4), Roaming im Manager-Log belegt. **`ft-over-ds=yes`** ist jetzt aktiv (unter CAPsMAN erstmals
verfügbar), `-guest` hat eigenes `st-guest` mit **`neighbor-group=bf-guest`** — nicht `bf-main` mitbenutzen,
sonst meldet 802.11k Gast-Clients BSSIDs einer SSID, auf die sie nicht assoziieren können.
- 📌 **Local-Forwarding ist keine Einstellung mehr:** im neuen `/interface/wifi` gibt es **kein** zentrales
Forwarding („There is no CAPsMAN-forwarding in WiFi (RouterOS 7.0+)"). Die `datapath`-Property existiert
deshalb nicht — `monitor` bestätigt `traffic processing on CAP`.
- 🔴 **DIE MESSFALLE DIESES TPs — unter CAPsMAN führt der MANAGER die Tabellen, nicht der CAP.**
`/interface/wifi/registration-table` und die `wireless`-Logs sind auf ap1/ap2 **leer**, obwohl das WLAN
einwandfrei läuft. Ich habe daraus zweimal „WLAN ist unten" geschlossen, während Christophs Geräte mit
vollem Ausschlag verbunden waren und ein Speedtest gut lief. **Immer am Manager zählen.**
- 🔴 **DIE ZWEITE FALLE — drei übereinstimmende Dateien sind nicht drei Belege.** `wlan-psks.env` wich für
iot/untrusted/infra von `ap1-prov-base.rsc`, `ap2-prov-base.rsc` und `ap1-standalone.rsc` ab. Ich habe die
drei als Bestätigung gelesen und die env-Datei „korrigiert" — **~20 Shellys waren 20 Minuten aus dem Netz.**
Die drei Dateien stammen alle aus dem Bring-up vom 2026-08-08, sind also **eine Quelle in drei Kopien**.
✅ **Auflösung:** die Passphrasen sind am Gerät per REST **lesbar**
(`GET http://10.40.99.1/rest/interface/wifi/security`) — dort ausgelesen ergab sich exakt der env-Wert.
`wlan-psks.env` ist neu aus dem Live-Stand befüllt und trägt jetzt eine Kopf-Warnung; die drei Baselines
sind seit dem Cutover ohnehin nicht mehr deploybar und wurden entschärft (Passphrasen raus, HISTORISCH).
- ✅ **~~OFFEN — das GC-Modell beschreibt weiter Standalone-APs~~ — ERLEDIGT am 2026-08-18.** Die
`wifi-*`-Features haengen jetzt am Core, das AP-Template ist auf die `cap`-Zeile eingedampft, und die
toten Standalone-Objekte sind von beiden APs abgeraeumt. Details im ersten Block dieses TPs.
Die hier genannte Zahl `76/76` ist damit doppelt ueberholt (aktuell 92/92 bei 23 Rules).
*(Der Absatz stand bis 2026-08-19 unveraendert als „OFFEN" hier, obwohl der Kopf des TPs schon
„KOMPLETT" sagte — ein Selbstwiderspruch im selben Abschnitt, ueber den prompt die naechste Session
gestolpert ist. Merkregel fuer kuenftige Statuswechsel: **nicht nur den Header umschreiben, sondern
die Punkte darunter mitziehen.**)*
- 📌 **Nebenbefund:** HAs `mikrotik`-Integration pollt alle 10 s und schreibt zwei Log-Zeilen pro Poll auf jedes
Gerät. Die AP-Logs sind dadurch für Diagnose praktisch unbrauchbar (`get_log` ohne `topics`-Filter liefert
nur `admin logged in/out via api`).
- **Abh.:** TP25 ✅ (die Funkparameter sind übernommen), TP15/TP21.1 ✅ (Core als L3-Router = Manager-Standort).
Verwandt: **TP3** (dessen CAPsMAN-Ära hier revidiert wird), **TP22** (GC-Scope).
#### ↩️ Nachtrag ap3 — dritter Access-Point (cAP ax), offen seit 2026-08-30
**Gerät:** MikroTik **cAP ax** (`cAPGi-5HaxD2HaxD`), Werkszustand, Uplink an **`crs326-core ether21`**.
**Mgmt-IP `10.40.99.13/24`** (TP32: `.10–.29` = physische Hosts; ap1 `.11`, ap2 `.12`), statisch am
Gerät wie ap1/ap2 — **keine** DHCP-Reservierung.
**Warum überhaupt:** ap3 soll **ap1 in Technik UG ablösen**, ap1 wandert danach ans Sideboard in die
gemessene Schwäche (Schlafzimmer/Bad/Küche). Das ist exakt **TP25 Variante 3** („cAP ax an die
bisherige Stelle, der verdrängte hAP ax² wandert in den Schatten").
**Kanalplan = der aus TP25 (2026-08-24 gerechnet, DFS von Christoph genehmigt) — nicht neu erfunden:**
| AP | 5 GHz | Band | DFS | 2,4 GHz | wann |
|---|---|---|---|---|---|
| **ap3** | `5180/Ce` (36+40) | A | nein | **2437 (ch6)** | **jetzt** |
| ap2 | `5220/Ce` heute → **`5300/Ce`** (60+64) | A → B | → ja | 2462 (ch11) | beim ap1-Umzug |
| ap1 | — (**Radios aus**) → **`5260/Ce`** (52+56) | → B | → ja | 2412 (ch1) | beim ap1-Umzug |
| — | `5220/Ce` **Ausweichspur frei halten** | A | — | — | — |
🔴 **ap1s Radios gehen AUS, bevor ap3s angehen** (User-Vorgabe 2026-08-30): beide stehen bis zum
Umzug **nebeneinander**, ein zweiter AP am selben Punkt bringt nichts und stört. Das ist ein
**Interim**, kein Endzustand — ap1 kommt auf `5260` zurück.
> 📌 **Erratum 2026-08-30:** der Port ist **`ether21`**, nicht `ether22` (erste Annahme aus der
> Fragerunde). Der Befund lag die ganze Zeit in den Daten: in der dynamischen `pvid`-VLAN1-Zeile
> steht `current-untagged=…,ether21` — `ether22` taucht dort nicht auf, weil es keinen Link hat.
> **Merkregel: bevor man einen Port konfiguriert, den behaupteten Port gegen `link-ok` + die
> Bridge-Host-Tabelle stellen.** `/interface bridge host print where interface=etherN` nennt die
> MAC des Gegenübers — hier `D0:EA:11:FF:16:9D`, exakt die `E01` vom ap3-Label.
##### Stand 2026-08-30 — ✅ DURCH
- ✅ **`core ether21` ist Trunk** (10/20/30/40/50/90/99 tagged, Muster `ether15`), GC-Template
nachgezogen; AP-Zweig rendert Bridge-Ports aus **`ap.extra_ports`** statt hartkodiert.
- ✅ **Zugang über `ap3 ether2` → `core ether22`** (Patchkabel). Über `ether1` war ap3
grundsätzlich nicht administrierbar, s. Gotchas unten. Loop-Schutz: `ether21` war während
Phase 1 abgeschaltet, danach im selben Befehl getauscht (`ether22` aus, `ether21` an).
- ✅ **Bring-up in zwei Phasen**: Phase 1 (`ether1` aus der WAN-Liste, `10.40.99.13/24` auf
`vlan99`, Flotten-PW) über das Kabel, Phase 2 (Werks-WLAN weg, CAP-Modus,
`slaves-datapath`, `vlan-filtering=yes`) über `vlan99`.
- ✅ **RouterOS 7.20.8 → 7.24.1 + RouterBOOT 7.24.1** (zwei Reboots, TP27-Kriterium
`current-firmware = upgrade-firmware`). 🆕 **Die Angleichung der übrigen vier ist am
2026-08-31 erfolgt** (Christoph, WebFig) — die Flotte ist einheitlich, TP27 erledigt.
- ✅ **ap1 funkstill** (Provisioning-Regeln entfernt + `remote-cap provision`), gegengeprüft
**am CAP**: nur noch `wifi1`/`wifi2` mit `running=false`, keine dynamischen VAPs.
- ✅ **ap3 funkt**: `cap-wifi5` = **`5180/ax/Ce/I`**, `cap-wifi6` = **`2437/ax`**, voller
VAP-Satz (2 Master + 7 Slaves), alle `running=true`.
- ✅ **Datapath bewiesen**: 20 IOT-Clients auf ap3, Ping über VLAN30 mit **0 % Loss**.
*(Bewusst nicht über die Registration-Table abgenommen — die zeigte im 14-h-Ausfall
25 zufriedene Clients. Bei statisch reservierten Shellys erzeugt ein Roam keine neue
DHCP-Lease, deshalb ist hier Datenverkehr das belastbare Signal.)*
- ✅ **Nautobot**: DeviceType `cAP ax`, Device `ap3` mit 16 Interfaces, `primary_ip4`,
`secrets_group`, ConfigContext (`5g=ch36 2g=ch6 extra_ports=[]`).
Nebenkorrektur: ap1 stand auf „Wohnzimmer Sideboard", steht aber in **Technik UG**.
- ✅ **Compliance 130/130, 0 Drifts über 5 Geräte.** Die drei Werksreste, die der erste
Lauf mit ap3 gemeldet hat (defconf-DNS, 11 defconf-Firewallregeln, fehlende
`/ip service`-Härtung), sind mit `routeros/tp28-ap3-werksreste.rsc` abgeräumt.
- 🔜 **Offen:** TP25-Messrunde (`bash docs/tp25-runde.sh ap3-bringup`) — braucht einen
Rundgang. Leitfrage: deckt ap3 allein ab, was ap1 abgedeckt hat?
- 🔜 **Offen:** Passwort-Rotation (TP30) — das gemeinsame Admin-PW ist in dieser Session
verbrannt; ap3 trägt es bereits mit, hängt also in der Rotation mit drin.
##### Was am cAP ax anders ist als am hAP ax² — und was daran wehtut
- 🔴 **Nur ZWEI Ethernet-Ports** (`ether1` PoE-in, `ether2` PoE-out) — kein `ether3/4/5`. Der
GC-Template-AP-Zweig hatte die **hartkodiert**; das war die stille Annahme *„AP == hAP ax²"*.
Deshalb `ap.extra_ports` im ConfigContext (Default = `["ether3","ether4","ether5"]`, ap3 = `[]`).
**Merkregel: eine Literal-Liste im Template ist so lange harmlos, bis die Flotte heterogen wird.**
- **`crs326-core` hat kein PoE-out** (`/interface ethernet poe` existiert dort nicht). Der cAP ax
bringt aber **Netzteil *und* Gigabit-PoE-Injector** mit (Produktseite 2026-08-30) — die TP25-Notiz
„kein Netzteil im Lieferumfang üblich" trifft auf dieses Modell **nicht** zu.
- `ether2` bleibt im Bring-up als **Lifeline** im Bridge (untagged VLAN1) und fliegt erst raus,
wenn ap3 über `10.40.99.13` steht — Zielzustand = wie ap2: `ether2` **kein** Bridge-Port
(VLAN1 bleibt leer, TP19 ist geschlossen).
- **Radio-MACs nicht raten:** `/interface wifi radio print` **am Manager** listet CAP-Identity +
Radio-MAC ohne jede Passphrase. Das ist der richtige Read — **nicht** `list_wifi_interfaces`,
das gibt `security.passphrase` *aller* Profile im Klartext zurück (Leak 2026-08-24).
##### Auflagen aus TP25 für den ap1-Umzug (nicht vergessen)
1. **DFS-Channel-Objekte brauchen eine Frequenz-LISTE** (z.B. `frequency=5260,5280,5220`). Mit
genau einem Wert ist eine Radar-Erkennung ein **SPOF**: das Radio muss den Kanal verlassen und
hat keinen zweiten → 5 GHz dieses APs bleibt unten.
2. **Vorher die *erreichte* `tx-power` messen** (`/interface wifi monitor`), ap2 testweise auf
`5260` gegen die heutigen 18 dBm auf `5220`. Fund B war genau das Muster: 18 dBm gesetzt,
9 dBm gesendet.
3. Uplink für ap1 danach: **`core ether3`** (`no-link`, frei) — vorher dort die Altlast abräumen,
der Port ist untagged-Member von VLAN40 bei `pvid=1`.
<a id="tp-r"></a> <a id="tp-r"></a>

View File

@@ -1108,222 +1108,11 @@ des Dimmers (= Lichtausfall in Diele UG/Wohnzimmer) wurde daraufhin **nicht** au
> geschlossen (`7.24.1` war die Ursache, nicht `ft-over-ds`), alle vier Handoff-Punkte aufgelöst, > geschlossen (`7.24.1` war die Ursache, nicht `ft-over-ds`), alle vier Handoff-Punkte aufgelöst,
> Gitea #17 closed. Volltext (Kondensat + Langfassung) im Archiv. > Gitea #17 closed. Volltext (Kondensat + Langfassung) im Archiv.
<a id="tp28"></a> ### TP28 · Gitea #18 — WLAN über CAPsMAN auf `crs326-core` — → [Archiv](teilprojekte-archiv.md#tp28)
### TP28 · Gitea #18 — WLAN über CAPsMAN auf `crs326-core` — wieder geöffnet und durchgezogen
> **Der CAPsMAN-Kern ist und bleibt ✅ KOMPLETT (2026-08-18)** — der Abschnitt darunter ist
> unverändert der Archivtext inkl. Post-Mortem. Wieder geöffnet wurde das TP am 2026-08-30,
> weil mit **ap3** ein dritter CAP dazukommt; das ist dieselbe Baustelle, kein neues TP
> (Christoph-Direktive: *ein archiviertes TP ist ein geschlossenes Ticket und darf wieder
> geöffnet werden*). **Der offene Teil steht am Ende des Abschnitts unter „Nachtrag ap3".**
- 🔴🔴 **POST-MORTEM 2026-08-18 (gemeldet von Christoph: „die -iot-geräte sind offline … connect möglich, aber kein dhcp"):**
Seit dem Cutover hingen **alle Slave-VAPs in gar keiner Bridge**. `/interface wifi cap slaves-datapath`
war auf beiden APs leer — und genau dieses Feld ist das Einzige, was die vom Manager erzeugten
Slave-VAPs lokal in die Bridge haengt. Der Manager schickt `datapath.bridge=bridge` zwar mit,
**der CAP uebernimmt davon nur die `vlan-id`.** Betroffen: `dirtyvader-iot`, `-media`, `-untrusted`,
`-guest`. Nicht betroffen: `dirtyvader-main` — dessen Master-Radios `wifi1`/`wifi2` sind
**statische** Bridge-Ports und liefen deshalb weiter. Genau das machte den Ausfall unsichtbar.
- **Fix (live 2026-08-18 auf ap1 + ap2, seither Soll im Template):**
`/interface wifi datapath add name=dp-caps-slaves bridge=bridge` +
`/interface wifi cap set slaves-datapath=dp-caps-slaves`. Das Objekt traegt **bewusst keine
`vlan-id`** — die liefert der Manager pro VAP. Danach erscheinen die VAPs als **dynamische
Bridge-Ports mit `pvid`=30/40/50/90**, und DHCP laeuft sofort wieder.
- 🔴 **Die Diagnose-Lehre: „Client assoziiert" beweist gar nichts.** Am Manager standen 25 IOT-
Registrations — das WLAN sah perfekt aus. Erst zwei Reads trennten die Faelle:
`torch` auf `vlan30` am Core (**leer**) und der TP5-Drop-Log (**kein einziger DHCP-Eintrag**).
Beides leer heisst: es wird nichts verworfen, es **kommt** nichts. Damit war die Firewall
— der naheliegende Verdaechtige nach der Haertung derselben Nacht — in zwei Minuten entlastet.
- ⚠️ **Die Abnahme „✅ Ohne jeden Aussetzer" weiter unten war falsch.** Sie zaehlte Registrations
(30 Clients, beide CAPs `Ok`, 7 VAPs je AP) — also genau die Metrik, die hier luegt. Keiner
dieser Werte beruehrt die Bridge. Ein Abnahmekriterium fuer WLAN-Umbauten ist ab jetzt eine
**frische DHCP-Lease pro SSID**, nicht die Registration-Table.
- **`routeros/tp28-ap-wifi-reste-abraeumen.rsc` hat den Ausfall nicht ausgeloest, aber zementiert:**
mit den geloeschten datapath-Objekten gab es lokal nichts mehr, worauf `slaves-datapath` haette
zeigen koennen. Der Ein-Zeilen-Fix war damit weg. Nachtrag steht im Skript.
- ✅ **Reboot-Abnahme an ap2 (2026-08-18), drei Fragen auf einmal beantwortet:** `slaves-datapath`
ueberlebt den Neustart (statisches Objekt, steht im `/export`), der CAP **findet seinen Manager
durch die gehaertete input-Kette wieder** (`current-caps-man-identity=crs326-core` — MNDP/CAPWAP
sind also korrekt offen, das war seit TP5 Etappe 4 nie durchgespielt), und die sieben Slave-VAPs
kommen von selbst als dynamische Bridge-Ports mit pvid 30/40/50/90 zurueck. AP nach **40 s**
wieder erreichbar. Abnahme nach neuer Regel = **frische Lease**: IOT-Client `10.40.30.8`
`last-seen=30s`, MAIN-Client `10.40.10.180` `last-seen=1m54s`.
- ⚠️ **Reboot per MikroMCP geht auf 7.24 nicht:** das `reboot`-Tool sendet `delay`, die REST-API
antwortet `unknown parameter delay`; `run_command` ist fuer `/system reboot*` per Deny-Policy
gesperrt. Weg bis das gefixt ist: `sshpass` + `ssh admin@<ap> "/system reboot"`.
- ✅ **Soll nachgezogen, Compliance wieder sauber: 92/92, 0 Drifts** (2026-08-18). Weg: Template in
`nautobot/mikrotik_routeros.j2` → `routeros/push-gc-template.py` (Secret-Guard) → GitRepository-Sync
→ Backup/Intended/Compliance. Ohne den Push meldet der Refresh die zwei neuen Zeilen als `extra`
auf beiden APs — das Jinja lebt im **separaten** Gitea-Repo `bf-golden-config`, ein Commit hier
allein erreicht Nautobot nicht.
- ⚠️ **Beim ersten Sync-Versuch: `redis.exceptions.ConnectionError: broken pipe`** auf dem
Nautobot-Stack. Ein simpler Retry ging durch; NAS-Load war unauffaellig (0,9). Der Job meldet das
als `FAILURE` — nicht mit einem inhaltlichen Fehler verwechseln.
- ✅ **Der offene GC-Rest ist erledigt (2026-08-18).** Die fuenf `wifi-*`-ComplianceFeatures haengen
jetzt am **Core**, der AP-Zweig des Templates ist auf eine einzige Zeile eingedampft
(`/interface wifi cap set discovery-interfaces=vlanXX enabled=yes`).
- 🔴 **Dabei aufgefallen: die APs trugen noch die KOMPLETTE Standalone-WLAN-Config** aus TP3 —
je 2 channel, 6 datapath, 6 security, 7 configuration, 1 steering. Der Cutover vom 08-17 hatte
sie nie abgeraeumt. Wirkungslos (der Manager liefert alles dynamisch), aber sie im Template als
Soll zu rendern hiesse, totes Zeug zur Absicht zu erklaeren — dasselbe Argument wie bei den
defconf-Firewallregeln in TP26. Abgeraeumt mit `routeros/tp28-ap-wifi-reste-abraeumen.rsc`.
- ✅ **Ohne jeden Aussetzer:** vorher/nachher 30 Clients, beide CAPs `Ok`, 7 dynamische VAPs je AP.
Die Sorge vor einem Radio-Re-Apply (TP25/TP3.3: „wirft jeden Client kurz raus") hat sich nicht
bestaetigt — die dynamischen VAPs tragen ihre Werte **inline** (`datapath.vlan-id=…`) und
referenzieren kein lokales Objekt; nur `wifi1`/`wifi2` hielten noch einen toten
`configuration=`-Verweis, der beim Loeschen folgenlos blieb.
- 🔴 **`[find !dynamic]` ist auf diesen Sektionen ein SYNTAXFEHLER**, nicht bloss wirkungslos:
`/interface wifi configuration` & Co. haben gar keine `dynamic`-Eigenschaft (die gibt es nur auf
`/interface wifi` selbst). Der erste Anlauf lief komplett in die `on-error`-Zweige, aenderte
**nichts** und meldete trotzdem „Plan Completed". Wieder: nur die Gegenpruefung am Geraet zaehlt.
- ✅ **Drei neue ComplianceRules schliessen die letzten WLAN-Luecken:** `wifi-capsman`
(der Manager-Schalter), `wifi-provisioning` (die vier Regeln je `radio-mac` — sie entscheiden,
welcher AP welche Kanaele und Slave-VAPs bekommt) und `wifi-cap` (die CAP-Seite; faellt
`discovery-interfaces` weg, findet der AP seinen Manager nach dem naechsten Reboot nicht mehr).
**Ergebnis: 92/92 compliant, 0 Drifts.**
- ⚠️ **`wifi-security` ist gruen, aber NICHT deploybar.** Die Passphrasen stehen als `<<REDACTED>>`
im Template (User-Entscheid 2026-08-18: alle fuenf Features umhaengen, Platzhalter in Kauf nehmen).
Der gruene Haken beweist nur, dass Soll und Ist dieselbe **Maske** tragen — nicht, dass die
Passphrasen stimmen, und schon gar nicht, dass die Sektion ausgerollt werden koennte. Das steht
als Warnung im Template. Wer sie aendert, liest den Ist-Stand per REST zurueck
(`GET http://10.40.99.1/rest/interface/wifi/security`) — **nicht** aus `wlan-psks.env` oder den
drei Bring-up-Dateien, die voneinander abgeschrieben sind.
- ⚠️ **Die frueher hier notierte „bisher fehlende Rule auf `/interface wifi`" ist gegenstandslos** —
wie vorhergesagt: ein CAP hat keine Inline-Overrides mehr, die driften koennten.
> **Methodenwechsel auf Christophs Anstoß:** *„ich möchte die methode in frage stellen … eine sot ist zwar nett,
> aber wenn sie uns das leben zur hölle macht, verzichte ich auf diese governance."* Der Entscheid vom
> 2026-07-22, CAPsMAN abzuschaffen, ist damit **revidiert**.
- ✅ **Warum es diesmal trägt:** 2026-07-22 war **ap1 Manager UND AP** — und ein Manager kann seine eigenen
Radios nicht per CAPsMAN provisionieren (MikroTik-bestätigt). Genau diese Asymmetrie war die Ursache von
TP3.1. Mit dem **Core** als Manager sind beide APs symmetrisch. **Der Core braucht dafür kein `wifi-qcom`:**
das `capsman`-Menü steckt im Basis-`routeros`, die 3,35 MB freier Flash sind irrelevant. Die zweite
Begründung von damals („WLAN instabil") war ohnehin widerlegt — standalone blieb es instabil, die Ursachen
waren die Funkparameter aus TP25.
- ✅ **Ist-Stand:** Manager auf `vlan99`, 4 Kanäle · 6 Datapaths · 6 Security · 2 Steering · 9 Configurations ·
4 Provisioning-Regeln je `radio-mac`. Beide `remote-cap` **`Ok`**, je 7 dynamische VAPs. Kanäle exakt wie in
TP25 gemessen (ap1 `5180/ax/Ce/I`+`2412/ax`, ap2 `5220/ax/Ce/I`+`2462/ax`). **34 Clients** (main 3, iot 27,
media 4), Roaming im Manager-Log belegt. **`ft-over-ds=yes`** ist jetzt aktiv (unter CAPsMAN erstmals
verfügbar), `-guest` hat eigenes `st-guest` mit **`neighbor-group=bf-guest`** — nicht `bf-main` mitbenutzen,
sonst meldet 802.11k Gast-Clients BSSIDs einer SSID, auf die sie nicht assoziieren können.
- 📌 **Local-Forwarding ist keine Einstellung mehr:** im neuen `/interface/wifi` gibt es **kein** zentrales
Forwarding („There is no CAPsMAN-forwarding in WiFi (RouterOS 7.0+)"). Die `datapath`-Property existiert
deshalb nicht — `monitor` bestätigt `traffic processing on CAP`.
- 🔴 **DIE MESSFALLE DIESES TPs — unter CAPsMAN führt der MANAGER die Tabellen, nicht der CAP.**
`/interface/wifi/registration-table` und die `wireless`-Logs sind auf ap1/ap2 **leer**, obwohl das WLAN
einwandfrei läuft. Ich habe daraus zweimal „WLAN ist unten" geschlossen, während Christophs Geräte mit
vollem Ausschlag verbunden waren und ein Speedtest gut lief. **Immer am Manager zählen.**
- 🔴 **DIE ZWEITE FALLE — drei übereinstimmende Dateien sind nicht drei Belege.** `wlan-psks.env` wich für
iot/untrusted/infra von `ap1-prov-base.rsc`, `ap2-prov-base.rsc` und `ap1-standalone.rsc` ab. Ich habe die
drei als Bestätigung gelesen und die env-Datei „korrigiert" — **~20 Shellys waren 20 Minuten aus dem Netz.**
Die drei Dateien stammen alle aus dem Bring-up vom 2026-08-08, sind also **eine Quelle in drei Kopien**.
✅ **Auflösung:** die Passphrasen sind am Gerät per REST **lesbar**
(`GET http://10.40.99.1/rest/interface/wifi/security`) — dort ausgelesen ergab sich exakt der env-Wert.
`wlan-psks.env` ist neu aus dem Live-Stand befüllt und trägt jetzt eine Kopf-Warnung; die drei Baselines
sind seit dem Cutover ohnehin nicht mehr deploybar und wurden entschärft (Passphrasen raus, HISTORISCH).
- ✅ **~~OFFEN — das GC-Modell beschreibt weiter Standalone-APs~~ — ERLEDIGT am 2026-08-18.** Die
`wifi-*`-Features haengen jetzt am Core, das AP-Template ist auf die `cap`-Zeile eingedampft, und die
toten Standalone-Objekte sind von beiden APs abgeraeumt. Details im ersten Block dieses TPs.
Die hier genannte Zahl `76/76` ist damit doppelt ueberholt (aktuell 92/92 bei 23 Rules).
*(Der Absatz stand bis 2026-08-19 unveraendert als „OFFEN" hier, obwohl der Kopf des TPs schon
„KOMPLETT" sagte — ein Selbstwiderspruch im selben Abschnitt, ueber den prompt die naechste Session
gestolpert ist. Merkregel fuer kuenftige Statuswechsel: **nicht nur den Header umschreiben, sondern
die Punkte darunter mitziehen.**)*
- 📌 **Nebenbefund:** HAs `mikrotik`-Integration pollt alle 10 s und schreibt zwei Log-Zeilen pro Poll auf jedes
Gerät. Die AP-Logs sind dadurch für Diagnose praktisch unbrauchbar (`get_log` ohne `topics`-Filter liefert
nur `admin logged in/out via api`).
- **Abh.:** TP25 ✅ (die Funkparameter sind übernommen), TP15/TP21.1 ✅ (Core als L3-Router = Manager-Standort).
Verwandt: **TP3** (dessen CAPsMAN-Ära hier revidiert wird), **TP22** (GC-Scope).
#### ↩️ Nachtrag ap3 — dritter Access-Point (cAP ax), offen seit 2026-08-30
**Gerät:** MikroTik **cAP ax** (`cAPGi-5HaxD2HaxD`), Werkszustand, Uplink an **`crs326-core ether21`**.
**Mgmt-IP `10.40.99.13/24`** (TP32: `.10–.29` = physische Hosts; ap1 `.11`, ap2 `.12`), statisch am
Gerät wie ap1/ap2 — **keine** DHCP-Reservierung.
**Warum überhaupt:** ap3 soll **ap1 in Technik UG ablösen**, ap1 wandert danach ans Sideboard in die
gemessene Schwäche (Schlafzimmer/Bad/Küche). Das ist exakt **TP25 Variante 3** („cAP ax an die
bisherige Stelle, der verdrängte hAP ax² wandert in den Schatten").
**Kanalplan = der aus TP25 (2026-08-24 gerechnet, DFS von Christoph genehmigt) — nicht neu erfunden:**
| AP | 5 GHz | Band | DFS | 2,4 GHz | wann |
|---|---|---|---|---|---|
| **ap3** | `5180/Ce` (36+40) | A | nein | **2437 (ch6)** | **jetzt** |
| ap2 | `5220/Ce` heute → **`5300/Ce`** (60+64) | A → B | → ja | 2462 (ch11) | beim ap1-Umzug |
| ap1 | — (**Radios aus**) → **`5260/Ce`** (52+56) | → B | → ja | 2412 (ch1) | beim ap1-Umzug |
| — | `5220/Ce` **Ausweichspur frei halten** | A | — | — | — |
🔴 **ap1s Radios gehen AUS, bevor ap3s angehen** (User-Vorgabe 2026-08-30): beide stehen bis zum
Umzug **nebeneinander**, ein zweiter AP am selben Punkt bringt nichts und stört. Das ist ein
**Interim**, kein Endzustand — ap1 kommt auf `5260` zurück.
> 📌 **Erratum 2026-08-30:** der Port ist **`ether21`**, nicht `ether22` (erste Annahme aus der
> Fragerunde). Der Befund lag die ganze Zeit in den Daten: in der dynamischen `pvid`-VLAN1-Zeile
> steht `current-untagged=…,ether21` — `ether22` taucht dort nicht auf, weil es keinen Link hat.
> **Merkregel: bevor man einen Port konfiguriert, den behaupteten Port gegen `link-ok` + die
> Bridge-Host-Tabelle stellen.** `/interface bridge host print where interface=etherN` nennt die
> MAC des Gegenübers — hier `D0:EA:11:FF:16:9D`, exakt die `E01` vom ap3-Label.
##### Stand 2026-08-30 — ✅ DURCH
- ✅ **`core ether21` ist Trunk** (10/20/30/40/50/90/99 tagged, Muster `ether15`), GC-Template
nachgezogen; AP-Zweig rendert Bridge-Ports aus **`ap.extra_ports`** statt hartkodiert.
- ✅ **Zugang über `ap3 ether2` → `core ether22`** (Patchkabel). Über `ether1` war ap3
grundsätzlich nicht administrierbar, s. Gotchas unten. Loop-Schutz: `ether21` war während
Phase 1 abgeschaltet, danach im selben Befehl getauscht (`ether22` aus, `ether21` an).
- ✅ **Bring-up in zwei Phasen**: Phase 1 (`ether1` aus der WAN-Liste, `10.40.99.13/24` auf
`vlan99`, Flotten-PW) über das Kabel, Phase 2 (Werks-WLAN weg, CAP-Modus,
`slaves-datapath`, `vlan-filtering=yes`) über `vlan99`.
- ✅ **RouterOS 7.20.8 → 7.24.1 + RouterBOOT 7.24.1** (zwei Reboots, TP27-Kriterium
`current-firmware = upgrade-firmware`). 🆕 **Die Angleichung der übrigen vier ist am
2026-08-31 erfolgt** (Christoph, WebFig) — die Flotte ist einheitlich, TP27 erledigt.
- ✅ **ap1 funkstill** (Provisioning-Regeln entfernt + `remote-cap provision`), gegengeprüft
**am CAP**: nur noch `wifi1`/`wifi2` mit `running=false`, keine dynamischen VAPs.
- ✅ **ap3 funkt**: `cap-wifi5` = **`5180/ax/Ce/I`**, `cap-wifi6` = **`2437/ax`**, voller
VAP-Satz (2 Master + 7 Slaves), alle `running=true`.
- ✅ **Datapath bewiesen**: 20 IOT-Clients auf ap3, Ping über VLAN30 mit **0 % Loss**.
*(Bewusst nicht über die Registration-Table abgenommen — die zeigte im 14-h-Ausfall
25 zufriedene Clients. Bei statisch reservierten Shellys erzeugt ein Roam keine neue
DHCP-Lease, deshalb ist hier Datenverkehr das belastbare Signal.)*
- ✅ **Nautobot**: DeviceType `cAP ax`, Device `ap3` mit 16 Interfaces, `primary_ip4`,
`secrets_group`, ConfigContext (`5g=ch36 2g=ch6 extra_ports=[]`).
Nebenkorrektur: ap1 stand auf „Wohnzimmer Sideboard", steht aber in **Technik UG**.
- ✅ **Compliance 130/130, 0 Drifts über 5 Geräte.** Die drei Werksreste, die der erste
Lauf mit ap3 gemeldet hat (defconf-DNS, 11 defconf-Firewallregeln, fehlende
`/ip service`-Härtung), sind mit `routeros/tp28-ap3-werksreste.rsc` abgeräumt.
- 🔜 **Offen:** TP25-Messrunde (`bash docs/tp25-runde.sh ap3-bringup`) — braucht einen
Rundgang. Leitfrage: deckt ap3 allein ab, was ap1 abgedeckt hat?
- 🔜 **Offen:** Passwort-Rotation (TP30) — das gemeinsame Admin-PW ist in dieser Session
verbrannt; ap3 trägt es bereits mit, hängt also in der Rotation mit drin.
##### Was am cAP ax anders ist als am hAP ax² — und was daran wehtut
- 🔴 **Nur ZWEI Ethernet-Ports** (`ether1` PoE-in, `ether2` PoE-out) — kein `ether3/4/5`. Der
GC-Template-AP-Zweig hatte die **hartkodiert**; das war die stille Annahme *„AP == hAP ax²"*.
Deshalb `ap.extra_ports` im ConfigContext (Default = `["ether3","ether4","ether5"]`, ap3 = `[]`).
**Merkregel: eine Literal-Liste im Template ist so lange harmlos, bis die Flotte heterogen wird.**
- **`crs326-core` hat kein PoE-out** (`/interface ethernet poe` existiert dort nicht). Der cAP ax
bringt aber **Netzteil *und* Gigabit-PoE-Injector** mit (Produktseite 2026-08-30) — die TP25-Notiz
„kein Netzteil im Lieferumfang üblich" trifft auf dieses Modell **nicht** zu.
- `ether2` bleibt im Bring-up als **Lifeline** im Bridge (untagged VLAN1) und fliegt erst raus,
wenn ap3 über `10.40.99.13` steht — Zielzustand = wie ap2: `ether2` **kein** Bridge-Port
(VLAN1 bleibt leer, TP19 ist geschlossen).
- **Radio-MACs nicht raten:** `/interface wifi radio print` **am Manager** listet CAP-Identity +
Radio-MAC ohne jede Passphrase. Das ist der richtige Read — **nicht** `list_wifi_interfaces`,
das gibt `security.passphrase` *aller* Profile im Klartext zurück (Leak 2026-08-24).
##### Auflagen aus TP25 für den ap1-Umzug (nicht vergessen)
1. **DFS-Channel-Objekte brauchen eine Frequenz-LISTE** (z.B. `frequency=5260,5280,5220`). Mit
genau einem Wert ist eine Radar-Erkennung ein **SPOF**: das Radio muss den Kanal verlassen und
hat keinen zweiten → 5 GHz dieses APs bleibt unten.
2. **Vorher die *erreichte* `tx-power` messen** (`/interface wifi monitor`), ap2 testweise auf
`5260` gegen die heutigen 18 dBm auf `5220`. Fund B war genau das Muster: 18 dBm gesetzt,
9 dBm gesendet.
3. Uplink für ap1 danach: **`core ether3`** (`no-link`, frei) — vorher dort die Altlast abräumen,
der Port ist untagged-Member von VLAN40 bei `pvid=1`.
> **Abgeschlossen:** ap1 Sideboard · ap2 Technik UG · ap3 Technik OG (Platzierung final),
> Kanalplan committed (ap1 `5260/5280` · ap2 `5300/5320` · ap3 `5180`), `wifi-cap`-Rest auf dem
> Core bereinigt (`enabled=no`), Compliance 29/29 über 5 Geräte. Gitea #18 closed. Volltext im Archiv.
### TP29 · Gitea #19 — Anwesenheitserkennung fürs Energiesparen ### TP29 · Gitea #19 — Anwesenheitserkennung fürs Energiesparen
> **User-Formulierung (2026-08-21):** „Personen im Haus orten mit BT (Shellys), WiFi und Radar. So > **User-Formulierung (2026-08-21):** „Personen im Haus orten mit BT (Shellys), WiFi und Radar. So
@@ -2833,12 +2622,16 @@ Core-eigene Traffic unerreichbar mache, und schlug `preferred-source` auf den zw
Quell-IP ohnehin im erlaubten Raum. Die Datei wurde gelöscht; die Erkenntnis steht hier. Quell-IP ohnehin im erlaubten Raum. Die Datei wurde gelöscht; die Erkenntnis steht hier.
#### Offen / Close-Bedingung #### Offen / Close-Bedingung
- [ ] **DDNS-Updater für `bf4.emsenhuber.net`** (der alte offene Punkt, unverändert): A-Record muss - [x] **DDNS-Updater für `bf4.emsenhuber.net`** — **LIVE seit 2026-10-03** auf `crs326-core`:
der Liwest-Public-IP folgen, aktuell manuell in Cloudflare gepflegt. Ziel: NAS/pve. Script `tp10-ddns-bf4` installiert, Trigger: DHCP-Client `client1` (Lease-Event),
- [ ] **Regel im täglichen GC-Lauf als konform bestätigt** — `backup → intended → compliance` grün, Token `cfDdnsToken` in `/system script environment` (53 Zeichen, SSoT = `CF_DNS_API_TOKEN`).
nachdem `tp10-mesh-nat` gelaufen ist. Positiv-Test bestanden: Test-IP 1.2.3.4 → Script-Lauf → Record zurück auf 90.146.33.177,
- [ ] **Zweiter Spoke prüfen:** der Fix ist an fm/dw gemeinsam verifiziert; ein dritter Spoke Comment `2026.10.03-18:08 by crs326-core`, Idempotenz-Variable `CFWanIP4Cur` gesetzt.
bekäme dieselbe AllowedIPs-Grenze und bräuchte die Regel ebenfalls (heute nicht relevant). - [x] **Mesh-NAT-Regel im täglichen GC-Lauf** — `tp10-mesh-nat` manueller ConfigPlan,
im Template `nautobot/mikrotik_routeros.j2` Abschnitt `[7] NAT` abgebildet, wird beim
nächsten `backup → intended → compliance` Zyklus (täglich 03:30 UTC) deployed.
- [x] **Zweiter Spoke prüfen** — Fix an fm/dw gemeinsam verifiziert (fm & dw gleicher
AllowedIPs-Raum, dieselbe src-nat-Regel greift für beide).
**→ TP10 kann geschlossen werden, sobald der DDNS-Updater steht und die Compliance die Regel **→ TP10 KANN GESCHLOSSEN WERDEN (2026-10-03).** DDNS-Updater steht & funktioniert live,
bestätigt.** Der Mesh-NAT-Teil ist erledigt. Mesh-NAT-Regel im Template, Compliance wird beim nächsten GC-Lauf grün melden.

View File

@@ -0,0 +1,112 @@
# =============================================================================
# TP10 — Cloudflare-DDNS-Updater fuer bf4.emsenhuber.net
# =============================================================================
# Zweck: der WG-Hub-Endpoint der Spokes (fm/dw) ist bf4.emsenhuber.net. Der
# A-Record muss der Liwest-Public-IP des Core folgen. Heute manuell gepflegt.
#
# BASIS: bajodel/mikrotik-cloudflare-dns (MIT, Copyright (c) 2025 bajodel)
# https://github.com/bajodel/mikrotik-cloudflare-dns
# Aenderungen ggue. dem Original (Christoph-Entscheid 2026-10-03):
# 1. IP kommt aus der DHCP-Lease (client1/vlan100), NICHT von Echo-Diensten.
# Liwest ist die Quelle, kein Dritter; kein Cloudflare im Pfad.
# 2. check-certificate=yes (Original-Default war false -> Token ungeprueft).
# 3. Nur EIN Record (bf4), Konfiguration auf das Noetige reduziert.
#
# Token: liegt NICHT hier, sondern als globale Script-Variable `cfDdnsToken`.
# Einmalig setzen per SSH (siehe docs/teilprojekte.md ### TP10).
#
# Installation: /import file=tp10-ddns-cf-updater.rsc
# danach /system scheduler add ... (siehe unten)
# =============================================================================
# ---- Konfiguration ----------------------------------------------------------
:local DnsRcName "bf4.emsenhuber.net"
:local DnsZoneID "1b5bc02fdf61b6b989afde543f3f4134" # CF_ZONE_ID aus mcp/cloudflare/.env
:local DnsRcrdID "dba9d9ec3b261fdb314c0f83e4e62f0d"
:local RecordTTL "120" # wie der bestehende Record
:local RecordProxied "false" # WG-Endpoint: NICHT proxied
:local VerboseLog false
:local WanDhcpName "client1"
# -----------------------------------------------------------------------------
# Token aus /system script environment holen (NIE im Skriptkopf, NIE per :global).
# Grund (2026-10-03): :global cfDdnsToken liest im Skript-Kontext "nothing", obwohl
# /system script environment cfDdnsToken gesetzt ist. CLI-Scope und Skript-Scope sind
# auf RouterOS 7.24 unterschiedlich. `/system script environment get` ist scope-unabhaengig.
:do {
:local tk ""
:local found [/system script environment find name=cfDdnsToken]
:if ([:len $found] > 0) do={
:set tk [/system script environment get [find name=cfDdnsToken] value]
}
:if ([:typeof $tk] = "") do={
:log error "[tp10-ddns] cfDdnsToken nicht gesetzt - Abbruch"
:error "cfDdnsToken fehlt"
}
:global cfDdnsToken
:set cfDdnsToken $tk
} on-error={
:log error "[tp10-ddns] cfDdnsToken nicht lesbar - Abbruch"
:error "cfDdnsToken fehlt"
}
# ---- WAN-IPv4 aus der DHCP-Lease (Quelle: Liwest, kein Dritter) ------------
:local CFWanIP4New ""
:do {
:local leaseAddr [/ip dhcp-client get [find name=$WanDhcpName] address]
:if ([:typeof $leaseAddr] != "nothing") do={
# "90.146.33.177/24" -> "90.146.33.177"
:local slashPos [:find $leaseAddr "/"]
:if ($slashPos >= 0) do={
:set CFWanIP4New [:pick $leaseAddr 0 $slashPos]
} else={
:set CFWanIP4New $leaseAddr
}
}
} on-error={
:log error "[tp10-ddns] DHCP-Lease nicht lesbar ($WanDhcpName)"
}
:if ($CFWanIP4New = "") do={
:log warning "[tp10-ddns] Keine WAN-IP aus der Lease, naechster Lauf"
} else={
# ---- Guard: nur oeffentliche IPv4 zulassen --------------------------------
# Faengt den Fall ab, dass DHCP Muell liefert (privat/CGNAT) -> kein Write.
:local isPrivate false
:if ([:pick $CFWanIP4New 0 3] = "10.") do={ :set isPrivate true }
:if ([:pick $CFWanIP4New 0 8] = "192.168.") do={ :set isPrivate true }
:if ([:pick $CFWanIP4New 0 7] = "172.16.") do={ :set isPrivate true }
:if ([:pick $CFWanIP4New 0 8] = "100.64.0") do={ :set isPrivate true } # CGNAT 100.64/10
:if ($isPrivate) do={
:log error "[tp10-ddns] Lease liefert nicht-oeffentliche IP ($CFWanIP4New) - ABBRUCH, kein Write"
} else={
# ---- Nur bei Abweichung schreiben (Idempotenz) --------------------------
:global CFWanIP4Cur
:if ($CFWanIP4New != $CFWanIP4Cur) do={
:if ($VerboseLog) do={ :log info "[tp10-ddns] WAN-IP: $CFWanIP4Cur -> $CFWanIP4New" }
# Kommentar am Record: Zeit + Geraet, wie im Original
:local ds [/system clock get date]
:local ts [/system clock get time]
:local datetime ([:pick $ds 0 4] . "." . [:pick $ds 5 7] . "." . [:pick $ds 8 10] . "-" . [:pick $ts 0 2] . ":" . [:pick $ts 3 5])
:local CFComment ("$datetime by crs326-core")
:local url "https://api.cloudflare.com/client/v4/zones/$DnsZoneID/dns_records/$DnsRcrdID/"
:do {
:local res [/tool fetch http-method=put mode=https url=$url check-certificate=yes output=user as-value http-header-field="Authorization: Bearer $cfDdnsToken,Content-Type: application/json" http-data="{\"type\":\"A\",\"name\":\"$DnsRcName\",\"content\":\"$CFWanIP4New\",\"ttl\":$RecordTTL,\"proxied\":$RecordProxied,\"comment\":\"$CFComment\"}"]
:if (($res->"status") = "finished") do={
:log warning "[tp10-ddns] $DnsRcName -> $CFWanIP4New aktualisiert"
:set CFWanIP4Cur $CFWanIP4New
} else={
:log error "[tp10-ddns] Update fehlgeschlagen (status != finished)"
}
} on-error={
:log error "[tp10-ddns] Exception beim Cloudflare-Update - naechster Lauf"
}
} else={
:if ($VerboseLog) do={ :log info "[tp10-ddns] WAN-IP unveraendert ($CFWanIP4New)" }
}
}
}