diff --git a/CLAUDE.md b/CLAUDE.md index 20694bf..e64724d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -77,7 +77,8 @@ GW je VLAN = `.1` (crs326-core L3) · Mgmt VLAN99: core `.99.1`, branch `.99.2`, - **Local Forwarding ist keine Einstellung mehr:** im neuen `/interface/wifi` gibt es kein zentrales Forwarding, `datapath` existiert dort nicht. - mgmt-VLAN-Wechsel am Switch = Lockout-Risiko: VLAN muss existieren + Port muss es tragen, in einem Schritt. - 🔥 **Die Firewall ist seit 2026-08-18 deny-by-default — in BEIDEN Ketten.** Ein neuer Dienst über eine VLAN-Grenze braucht ab jetzt eine explizite Regel, sonst funktioniert er einfach nicht. Diagnosewerkzeug ist die **Log-Regel direkt vor dem Drop** (`TP5-WOULD-DROP` / `TP5-INPUT-WOULD-DROP`), die weiterläuft und in den Kollektor `10.40.20.18:514` schreibt (Dateien auf dem Docker-LXC unter `/opt/tp5-syslog/log/`). **Erst dort nachsehen, bevor irgendwo anders gesucht wird.** Ad-hoc-Zugriff aus MAIN in ein anderes VLAN (Scannen, IoT anlernen) läuft über die selbstverfallende Liste: `/ip firewall address-list add list=jit-admin address= timeout=1h`. -- ⏰ **Zeitquelle ist der Core** (`/system ntp server`, seit 2026-08-18). Vorher zeigten alle drei anderen Geräte auf `10.40.99.1`, wo **kein** NTP-Server lief (`status=waiting`) — ap2 war dadurch **17 Stunden** versetzt und seine Logs für jede Korrelation wertlos. Merkregel: bei widersprüchlichen Zeitstempeln zwischen Geräten zuerst `/system ntp client get status` prüfen, nicht die Logik. +- ⏰ **Zeitquelle ist der Core** (`/system ntp server`, seit 2026-08-18), ausgeliefert per **DHCP-Option 42** (`ntp-server=` in jedem der 7 Netze) und drift-überwacht (ComplianceRule `system-ntp`). Vorher lief **nirgends** eine Synchronisation: die anderen drei Geräte zeigten auf `10.40.99.1`, wo kein Server lief, und der Core selbst hatte gar keinen NTP-Client — ap2 war dadurch **17 Stunden** versetzt. 🔴 **Zwei Fallen dabei:** (a) ein NTP-Server mit `use-local-clock=no` und unsynchronisiertem Client liefert **Stratum 16**, das jeder Client verwirft — Server einschalten allein reicht nicht; (b) wird der Server eingeschaltet, *bevor* sein Client synchron ist, hält er diesen Zustand fest → **`enabled=no`/`=yes` nach der Synchronisation**. Merkregel: bei widersprüchlichen Zeitstempeln zwischen Geräten zuerst `/system ntp client get status` lesen, und zum Eingrenzen einen Client testweise auf eine öffentliche Quelle zeigen lassen. +- 📡 **SSDP-Relay für die macOS-Sonos-App:** LXC **108 `mcast-relay`** auf pve, zweibeinig `10.40.10.21` (VLAN10) + `10.40.40.21` (VLAN40), `multicast-relay` mit `--noMDNS` (mDNS macht der Core) und `--ssdpUnicastAddr`. **Zwei Beine sind zwingend** — SSDP ist link-lokal mit TTL 1, ein Relay muss Mitglied beider Broadcast-Domänen sein; Routing hilft nicht. ⚠️ **Aus dem Relay heraus lässt sich der Relay nicht testen** (Loop-Schutz ignoriert eigene Quelladressen) — am echten Client testen oder `journalctl -u multicast-relay` mitlesen. - 🐳 **Nautobot auf der DS923 braucht einen abgeschalteten Image-Healthcheck.** Der eingebackene Check ruft alle 10 s `nautobot-server health_check` = einen vollen Django-Start; unter Last dauert der >2 min, die Aufrufe **stapeln sich unbegrenzt** → Speicher alle → `fork()` mit ENOMEM → DSM räumt den ganzen Stack ab (am 2026-08-17 zweimal passiert, Load bis 73 bei 4 Kernen). Im Stack stehen deshalb `healthcheck: disable: true` auf worker+scheduler, `--concurrency=2` und `mem_limit`. **`cpus:` geht auf Synology nicht** (Kernel ohne CFS-Quota, der Stack-Deploy schlägt sonst fehl). - 📜 **~~Omada ES208G = Easy Managed~~ — gegenstandslos** (Gerät abgebaut 2026-07-23, Controller gelöscht). - **L3-Router = `crs326-core` (TP21.1 ✅ 2026-08-08, ersetzt ap1).** Bridge vlan-filtering=yes; VLAN-SVIs `.1` je VLAN mit DHCP (7 Server / 54 Leases); DNS über **DoH** zum CF-Gateway, `verify-doh-cert=yes` (TP23 Stufe 1 ✅ 2026-08-18). Die alte Notiz „core hat kein CA" war zu pessimistisch: es braucht kein Bundle, sondern **genau einen Root** (GTS Root R4, ~1 KB) — die Kette ist Leaf → GTS WE1 → R4. **Beide Firewall-Ketten enden seit 2026-08-18 mit `drop`** (TP5 E3/E4); `TP10-seal-lan` ist `disabled`, Mgmt-Input kommt nur noch aus VLAN10, von HA (`8728`), von Nautobot (`22`) und über `cf-mesh`. **ap1/ap2 sind NUR noch APs** — kein DHCP/DNS/NAT/Routing/WG dort. (Die frühere „TP4: ap1 ist L3-Router"-Zeile ist überholt.) diff --git a/docs/teilprojekte.md b/docs/teilprojekte.md index 0197763..83cb998 100644 --- a/docs/teilprojekte.md +++ b/docs/teilprojekte.md @@ -368,7 +368,29 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung". iOS-App (mDNS) und HA-Integration decken den Nutzungsbedarf ab; Spotify Connect läuft über Cloud. - Abh.: TP4 ✅, TP5 (Sonos-Holes) ✅ live. -### TP7.1 — SSDP-Reflector (macOS-Sonos-App über VLAN-Grenze) ▸ 🔜 **IN TP5 GEZOGEN (User-Vorgabe 2026-08-14)** — nicht mehr „vorgemerkt" +### TP7.1 — SSDP-Relay (macOS-Sonos-App über VLAN-Grenze) ▸ ✅ **LÄUFT (2026-08-18)** — User-bestätigt: „sonos ist da!" +- ✅ **Umsetzung:** eigener LXC **108 `mcast-relay`** auf pve (Ubuntu 24.04, 1 Kern, 256 MB, `onboot`), + zweibeinig: `eth0` = **10.40.10.21** (VLAN10), `eth1` = **10.40.40.21** (VLAN40), Default-Route nur + über VLAN10, **kein IP-Forwarding**. Dazu `core ether13` (pve-Port) um `tagged vlan10, vlan40` + erweitert (`routeros/tp7.1-core-ether13-trunk.rsc`). Software: `alsmith/multicast-relay` als + systemd-Unit `multicast-relay.service`. + - **`--noMDNS` ist Pflicht:** mDNS reflektiert bereits der Core (TP7). Zweimal zu reflektieren + erzeugt Duplikate. **`--ssdpUnicastAddr 10.40.10.21`** ist ebenfalls noetig — die Sonos + antworten auf einen M-SEARCH per **Unicast**, und diese Antworten muessen zurueckgereicht werden. +- 🔑 **Warum zwei Beine zwingend sind** (Christophs Frage „vlan40 sollte keinen relay benoetigen?!?"): + SSDP ist **link-lokales Multicast mit TTL 1**. Der M-SEARCH des MacBooks verlaesst VLAN10 nie, die + `ssdp:alive`-NOTIFYs der Sonos verlassen VLAN40 nie. Ein Relay kann nur kopieren, was es selbst + hoert — es **muss** Mitglied beider Broadcast-Domaenen sein. Routing hilft wegen TTL 1 nicht, und + `forward accept vlan10<->vlan40` ist laengst offen: es scheitert **vor** dem Verbindungsaufbau, an + der Entdeckung. Dass AirPlay funktionierte, passt genau ins Bild — das ist mDNS. +- 🎯 **Beweis im Log:** `[Sonos Discovery] Relayed 268 bytes from 10.40.10.183:49358 on eth0 [ttl 64] + to 255.255.255.255:1900 via eth1/10.40.40.21` — echte Suchrufe beider Macs (`.10.183`, `.10.82`). +- ⚠️ **Falle beim Selbsttest:** ein M-SEARCH **vom Relay-Host selbst** wird nicht weitergereicht + (Loop-Schutz: das Werkzeug ignoriert eigene Quelladressen). Mein synthetischer Test meldete + deshalb 0 Antworten, waehrend die App bereits funktionierte. **Aus dem Relay heraus laesst sich + der Relay nicht pruefen** — entweder am echten Client testen oder am Log mitlesen. +- **Keine Firewall-Arbeit noetig:** der Relay spricht in beiden VLANs jeweils lokal; das trifft die + `forward`-Kette nicht. - 📌 **Status-Update 2026-08-14:** Symptom vom User erneut bestätigt — **iOS-App ✅, HA-Sonos-Panel ✅ (steuern/gruppieren), macOS-Desktop-App findet die Umgebung nicht.** Damit ist das Bild vollständig: alles, was **mDNS** spricht, funktioniert; nur der **SSDP**-Weg fehlt. Der Fix wird **Bestandteil des TP5-Pakets** (gemeinsamer Test mit Deny-by-default statt zweimal dieselbe Strecke prüfen). ⚠️ **Nicht als Firewall-Problem fehldiagnostizieren:** `forward accept vlan10↔vlan40` ist längst offen — es scheitert an der Discovery, nicht am Datenpfad. - **Bedarf:** macOS-Desktop-Sonos-App auf VLAN 10 findet die Sonos in VLAN 40 nicht (Discovery per SSDP UDP 1900, RouterOS reflektiert kein SSDP). iOS-App + HA decken den Alltag ab, aber User will die Desktop-App nutzen. @@ -694,14 +716,21 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung". Stelle** eine funktionierende Zeitsynchronisation; dass drei von vier Uhren stimmten, war Restglueck vom letzten manuellen Stellen. Behoben mit `routeros/tp19-ntp-core-client.rsc` (`time.cloudflare.com,at.pool.ntp.org`) → Core `synchronized`. - - ⏳ **Rest:** ap1/ap2/branch stehen weiterhin auf `status=waiting`, obwohl der Core jetzt eine - gueltige Quelle ist. ap2s Uhr wurde einmalig von Hand gestellt (17 h Versatz liegt ueber der - NTP-Panic-Schwelle, so weit springt ein Client nicht von selbst) — **alle vier Uhren stimmen - jetzt**, das Einrasten der Clients ist nachzukontrollieren. - - ⚠️ **Governance-Luecke, die dabei auffiel: `/system ntp` hat GAR KEINE Compliance-Rule.** - Die NTP-Konfiguration ist nicht drift-ueberwacht — exakt der Zustand, den **TP26** fuer - `/ip firewall filter` behoben hat. Gehoert nachgezogen, sonst faellt die naechste stille - Aenderung wieder erst ueber falsche Zeitstempel auf. + - 🔴 **Und der letzte Schritt war der unauffaelligste: der Server hielt Stratum 16 fest.** Er war + eingeschaltet worden, **waehrend sein eigener Client noch unsynchronisiert war** — RouterOS + bewertet das nicht von selbst neu. Die Anfragen kamen an (die Firewall-Regel zaehlte mit), + wurden aber unbrauchbar beantwortet, und alle drei Clients blieben auf `waiting`. + **Ein `enabled=no` / `enabled=yes` nach der Synchronisation hat es geloest.** + Der entscheidende Test dorthin: branch einmal auf `at.pool.ntp.org` zeigen lassen — es + synchronisierte in 20 Sekunden. Damit war bewiesen, dass der Client heil ist und der Server + schweigt, statt weiter im Nebel zu stochern. + ✅ **Endstand: alle vier Geraete `synchronized`.** (ap2s Uhr musste einmalig von Hand gestellt + werden — 17 h liegen ueber der NTP-Panic-Schwelle, so weit springt kein Client von selbst.) + - ✅ **Governance nachgezogen (2026-08-18):** NTP steht jetzt im **GC-Template** (Core als Quelle + mit oeffentlichem Upstream, branch/APs als Clients auf `10.40.99.1`), wird per **DHCP-Option 42** + (`ntp-server=` je Netz, Christophs Vorschlag) an **alle** Clients ausgeliefert — nicht nur an die + MikroTik-Flotte — und ist mit der neuen ComplianceRule **`system-ntp`** drift-ueberwacht. + Das war exakt der blinde Fleck, den **TP26** fuer `/ip firewall filter` geschlossen hat. - ✅ **Live-Nachprüfung 2026-08-16 (MikroMCP + CF-API, reine Reads):** - **`cudy1` ist tot, nicht nur „offline gemeldet":** `core ether11` steht auf `running=false`, **letzter Link-Down 2026-08-08 19:33**, seither kein Link. Die Formulierung „aktiver VLAN1-Leak" ist damit endgültig hinfällig — der **Dual-DHCP-Race hat keine Quelle mehr**. Offen bleibt nur der *physische* Ausbau + Inventar/Doku. - **`crs326-core` hat keine VLAN1-IP mehr:** `/ip/address` listet nur noch die 7 SVIs + die WAN-DHCP-Adresse — die **Bring-up-Altlast `10.40.41.211` ist weg**. Damit fällt einer der 5 Compliance-Drifts aus TP22 raus, *sobald* wieder **Backup → Intended → Compliance** gelaufen ist (die Compliance vergleicht gegen das letzte Backup, nicht gegen das Gerät — s. TP22-Falle). @@ -1015,7 +1044,12 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung". - **Abh.:** keine harten. **TP15 ✅** (die zu versorgende Hardware steht und hat DC-Eingänge). Verwandt: **TP18** (Garage-Nodes brauchen je Site eine Stromversorgung), **TP11** (USV-Zustand ins Monitoring). **Kein Blocker für TP5/TP6.** - **Historie:** war bis 2026-08-16 Unterpunkt von TP15 („🔋 DC-USV: MeanWell LRS-150-48 + DDR-120-48 + LiFePO4 20Ah (~€190), 48V-Bus; CRS326 DC-input passt"). Vollständige Recherche inkl. Schaltbild, Effizienzvergleich und Gerätetabelle: `docs/hw-refresh-requirements.md §9` (+ §10 D/E/F für die offenen Fragen). -### TP25 — WLAN-Stabilisierung: Best-Practice-Audit statt Bauchgefühl ▸ ⏳ **SCHRITTE 1–3 DURCH (2026-08-17): beide APs 18 dBm, 20 MHz auf 2,4, überlappungsfrei** — offen: Signal-Boden + Re-Walk — Sammel-TP für Christophs Beobachtungen 1–6 +### TP25 — WLAN-Stabilisierung ▸ ⏳ **Funkparameter + Drift-Erkennung durch; offen nur noch Messarbeit** — Sammel-TP für Christophs Beobachtungen 1–6 +> **Restliste (Stand 2026-08-18):** Fund A–D ✅ · Drift-Luecke ✅ (mit TP28, 92/92) · +> **offen: Fund E** (`access-list` mit `signal-range`-Boden — ⚠️ gehoert jetzt auf den **Manager**, +> nicht mehr auf die APs) · **Fund F** (5:05-Takt bei IoT-Trennungen, am Manager nachmessen — +> die AP-Tabellen sind unter CAPsMAN leer) · **Re-Walk + Dead-Spot-Kartierung** (hands-on). +> Alles drei ist Messarbeit gegen die korrigierte Basis, keine Konfigurationsarbeit mehr. > **Warum eigener TP:** Christoph hat am 2026-08-16/17 sechs konkrete Beobachtungen gemeldet und ausdrücklich > **einen** TP dafür verlangt. Sie hängen alle an derselben Ursache-Klasse (Funkparameter, die nicht nach Manual > gesetzt sind), gehören also zusammen. **TP25 übernimmt den offenen Rest von TP3.3** (Re-Walk, Dead-Spot-Kartierung) @@ -1240,6 +1274,11 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung". Deploy die Fixes **nicht** mehr zurückdrehen. *(Bemerkenswert: `width` für 2,4 GHz stand dort die ganze Zeit korrekt auf `20mhz` — die SoT war richtig, nur das Gerät hat sie nicht befolgt. Der Beweis, dass das Problem der Inline-Override war und nicht die Datenlage.)* + - ✅ **ERLEDIGT 2026-08-18 (mit dem TP28-GC-Umbau).** Die `wifi-*`-Features haengen jetzt am Core, + die APs sind auf die `cap`-Zeile eingedampft, und drei neue Rules (`wifi-capsman`, + `wifi-provisioning`, `wifi-cap`) decken den Rest ab. **92/92 compliant.** Die unten + beschriebene Luecke auf `/interface wifi` ist damit gegenstandslos: ein CAP hat keine + Inline-Overrides mehr. Der historische Befund bleibt als Begruendung stehen: - 🔴 **Der Erkennungsteil bleibt offen. Die 18 Compliance-Rules abgefragt — es fehlen genau zwei Sektionen:** | vorhanden | fehlt | |---|---| @@ -1319,7 +1358,38 @@ FT kaschierte das bisher als „Roaming" statt „schwache Abdeckung". liefert das Entscheidungskriterium). ⚠️ **Berührt TP5:** der Mesh-Verkehr braucht bei deny-by-default eine explizite `forward`-Zeile — s. TP5 Etappe 2. -### TP28 — WLAN über CAPsMAN auf `crs326-core` ▸ ✅ **CUTOVER DURCH (2026-08-17)** — beide APs sind reine CAPs +### TP28 — WLAN über CAPsMAN auf `crs326-core` ▸ ✅ **KOMPLETT (2026-08-18): Cutover + GC-Modell nachgezogen** +- ✅ **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 `<>` + 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**. diff --git a/nautobot/mikrotik_routeros.j2 b/nautobot/mikrotik_routeros.j2 index 31d5362..d1109f4 100644 --- a/nautobot/mikrotik_routeros.j2 +++ b/nautobot/mikrotik_routeros.j2 @@ -80,10 +80,13 @@ /interface bridge port add bridge=bridge comment=defconf interface=sfp-sfpplus2 # ether4 = Wohnzimmer-Sideboard: 2026-08-13 von Trunk auf untagged VLAN40 gezogen (TP21.1/4). /interface bridge vlan add bridge=bridge untagged=ether1,ether2 vlan-ids=1 -/interface bridge vlan add bridge=bridge tagged=ether1,ether8,ether15 vlan-ids=10 +# ether13 (pve) traegt VLAN10 + VLAN40 seit 2026-08-18: der SSDP-Relay-LXC fuer die +# macOS-Sonos-App braucht ein Bein in beiden (TP7.1). SSDP ist link-lokal mit TTL 1 — +# ein Relay muss Mitglied beider Broadcast-Domaenen sein, Routing hilft nicht. +/interface bridge vlan add bridge=bridge tagged=ether1,ether8,ether13,ether15 vlan-ids=10 /interface bridge vlan add bridge=bridge tagged=ether8,ether13,ether15 vlan-ids=20 /interface bridge vlan add bridge=bridge tagged=ether1,ether8,ether15 untagged=ether14 vlan-ids=30 -/interface bridge vlan add bridge=bridge tagged=ether1,ether8,ether15 untagged=ether2,ether3,ether4,ether5 vlan-ids=40 +/interface bridge vlan add bridge=bridge tagged=ether1,ether8,ether13,ether15 untagged=ether2,ether3,ether4,ether5 vlan-ids=40 /interface bridge vlan add bridge=bridge tagged=ether1,ether8,ether15 vlan-ids=50 /interface bridge vlan add bridge=bridge tagged=ether1,ether8,ether15 vlan-ids=90 /interface bridge vlan add bridge=bridge tagged=ether8,ether15,bridge vlan-ids=99 @@ -124,7 +127,7 @@ /ip dhcp-server add address-pool=pool-v{{ v.id }} comment={{ v.name }} interface=vlan{{ v.id }} lease-time={{ v.lease }} name=dhcp-v{{ v.id }} {% endfor %} {% for v in vlans %} -/ip dhcp-server network add address=10.40.{{ v.id }}.0/24 dns-server=10.40.{{ v.id }}.1 domain=bf.lan gateway=10.40.{{ v.id }}.1 +/ip dhcp-server network add address=10.40.{{ v.id }}.0/24 dns-server=10.40.{{ v.id }}.1 domain=bf.lan gateway=10.40.{{ v.id }}.1 ntp-server=10.40.{{ v.id }}.1 {% endfor %} /ip dhcp-server option add code=6 name="dns 1.1.1.1" value=0x01010101 @@ -142,6 +145,73 @@ # Artefakt: routeros/tp23-stufe1-doh-core.rsc /ip dns set allow-remote-requests=yes mdns-repeat-ifaces=vlan10,vlan20,vlan40,vlan30,vlan90 servers=1.1.1.1 use-doh-server=https://px35ynlol3.cloudflare-gateway.com/dns-query verify-doh-cert=yes +# [6a] ZEIT. Der Core ist die Zeitquelle des Standorts — und braucht dafuer selbst eine. +# Vor dem 2026-08-18 war /system ntp client AUS und der Server ebenfalls: die anderen drei +# Geraete zeigten auf 10.40.99.1 ins Leere (`status: waiting`), ap2 war 17 h versetzt. +# ⚠️ Ein NTP-Server mit `use-local-clock=no` und unsynchronisiertem Client liefert Stratum 16, +# das jeder Client verwirft — Server einschalten allein reicht also NICHT. +# Die Clients bekommen den Server zusaetzlich per DHCP (ntp-server=, s. [5]). +# Schreibweise flach-einzeilig, weil das GC-Backup so ablegt (nicht im Sektions-Stil von /export). +/system ntp client set enabled=yes +/system ntp client servers add address=time.cloudflare.com +/system ntp client servers add address=at.pool.ntp.org +/system ntp server set enabled=yes + +# [6b] WLAN — CAPsMAN. Seit TP28 (2026-08-17) ist der Core der Manager und ap1/ap2 +# sind reine CAPs. Die fuenf wifi-*-ComplianceFeatures gehoeren deshalb HIERHER +# und nicht mehr in den AP-Zweig; auf den APs ist die entsprechende Config +# abgeraeumt worden (sie war nach dem Cutover nur noch wirkungsloser Rest). +# +# 🔴 SECURITY IST NICHT DEPLOYBAR. Die Passphrasen stehen hier als +# "<>", weil TP22-Regel 1 maskierte Sektionen generell vom Push +# ausschliesst. Ein gruener Compliance-Haken auf `wifi-security` beweist +# deshalb NUR, dass Soll und Ist dieselbe Maske tragen — er beweist NICHT, +# dass die Passphrasen stimmen und schon gar nicht, dass die Sektion +# ausgerollt werden koennte. Wer sie wirklich aendern will, macht das am +# Geraet und liest die Werte per REST zurueck +# (`GET http://10.40.99.1/rest/interface/wifi/security`, s. TP28) — +# NICHT aus `wlan-psks.env` oder den drei alten Bring-up-Dateien, die +# voneinander abgeschrieben sind und schon einmal 20 Shellys aus dem Netz +# geworfen haben. +# +# Literal-Block wie die L2-Fabric: Radio-MACs und Kanalzuordnung sind +# geraetegebunden und in Nautobot nicht modelliert. Aenderungen daran sind +# bewusst sichtbare Template-Aenderungen. +/interface wifi channel add band=5ghz-ax frequency=5180 name=ch-5g-ap1 width=20/40mhz +/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 +/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 +/interface wifi datapath add bridge=bridge name=dp-media vlan-id=40 +/interface wifi datapath add bridge=bridge name=dp-untrusted vlan-id=50 +/interface wifi datapath add bridge=bridge name=dp-guest vlan-id=90 +/interface wifi security add authentication-types=wpa3-psk ft=yes ft-over-ds=yes name=sec-main passphrase="<>" +/interface wifi security add authentication-types=wpa2-psk,wpa3-psk disabled=no ft=no name=sec-infra passphrase="<>" +/interface wifi security add authentication-types=wpa2-psk,wpa3-psk disabled=no ft=no name=sec-iot passphrase="<>" +/interface wifi security add authentication-types=wpa2-psk,wpa3-psk ft=no name=sec-media passphrase="<>" +/interface wifi security add authentication-types=wpa2-psk,wpa3-psk disabled=no ft=no name=sec-untrusted passphrase="<>" +/interface wifi security add authentication-types=wpa2-psk,wpa3-psk disabled=no ft=no name=sec-guest passphrase="<>" +/interface wifi configuration add country=Austria datapath=dp-infra mode=ap name=cfg-infra security=sec-infra ssid=dirtyvader-infra +/interface wifi configuration add country=Austria datapath=dp-iot mode=ap name=cfg-iot security=sec-iot ssid=dirtyvader-iot +/interface wifi configuration add country=Austria datapath=dp-media mode=ap name=cfg-media security=sec-media ssid=dirtyvader-media +/interface wifi configuration add country=Austria datapath=dp-untrusted mode=ap name=cfg-untrusted security=sec-untrusted ssid=dirtyvader-untrusted +# st-guest hat eine EIGENE neighbor-group: mit bf-main wuerde 802.11k den Gaesten +# BSSIDs einer SSID melden, auf die sie gar nicht assoziieren koennen (TP28). +/interface wifi steering add name=st-main neighbor-group=bf-main rrm=yes wnm=yes +/interface wifi steering add name=st-guest neighbor-group=bf-guest rrm=yes wnm=yes +/interface wifi configuration add channel=ch-5g-ap1 country=Austria datapath=dp-main mode=ap name=cfg-main-5g-ap1 security=sec-main ssid=dirtyvader-main steering=st-main +/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 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 +/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 + # Kanonischer Mgmt-Nummernplan (User-Bestaetigung 2026-08-13, verbindlich): # core .99.1 | branch .99.2 | ap1 .99.11 | ap2 .99.12 /ip dns static add address=10.40.99.1 comment="core = GW/Router" name=router.lan type=A @@ -205,6 +275,8 @@ /interface vlan add comment=MGMT interface=bridge name=vlan99 vlan-id=99 /ip address add address={{ obj.primary_ip4.address }} comment=TP21-mgmt-target interface=vlan99 network={{ obj.primary_ip4.host.split(".")[:3] | join(".") }}.0 /ip dns set servers=10.40.99.1 +/system ntp client set enabled=yes +/system ntp client servers add address=10.40.99.1 /ip route add comment=TP21-default dst-address=0.0.0.0/0 gateway=10.40.99.1 # L2-FABRIC — wie beim Core ein Literal-Block (Begruendung dort). @@ -254,48 +326,21 @@ # Referenz: routeros/state-backups/ap1-gc-backup-scrubbed-20260813.cfg # ===================================================================================== -# [1] CHANNELS — Frequenz aus der Kanalnummer gerechnet (eine Quelle der Wahrheit) -{% for r in ap.radios %} -{% set ch = ap.channels[r.key] %} -{% set freq = (5000 + 5 * ch) if r.key == "5g" else (2407 + 5 * ch) %} -/interface wifi channel add band={{ r.band }} frequency={{ freq }} name=ch-{{ r.key }} width={{ r.width }} -{% endfor %} - -# [2] DATAPATH — je Zone ein VLAN-Tag. OHNE datapath egressiert die SSID ungetaggt -# in VLAN1 (das war das TP3.1-Leck) -> jede Zone MUSS hier auftauchen. -{% for z in ap.zones %} -/interface wifi datapath add bridge={{ ap.bridge }} name=dp-{{ z.name }} vlan-id={{ z.vlan }} -{% endfor %} - -# [3] SECURITY — Passphrasen tragen die Backup-Maske aus TP22 Schritt 0. -# ft-over-ds schreibt RouterOS nur, wenn ft=yes. -{% for z in ap.zones %} -/interface wifi security add authentication-types={{ z.auth }} ft={{ z.ft }}{% if z.ft == "yes" %} ft-over-ds={{ z.ft_over_ds }}{% endif %} name=sec-{{ z.name }} passphrase="<>" -{% endfor %} - -# [3b] STEERING — 802.11k (rrm: Neighbor Reports) + 802.11v (wnm: BSS Transition). -# OHNE das klebt der Client am sterbenden AP, bis die Verbindung abreisst; 802.11r/FT -# macht die Uebergabe nur SCHNELL, es loest sie nicht aus. Genau das fehlte nach dem -# TP3-Rebuild auf beiden APs und hat Videocalls beim Durchgehen abgerissen (TP3.3). -# `neighbor-group` ist site-weit identisch -- nur dann gelten ap1/ap2 als Nachbarn. -# Konstante statt ConfigContext: es gibt genau EINE Roaming-Domain pro Standort. -{% set steering = {"name": "st-main", "neighbor_group": "bf-main"} %} -/interface wifi steering add name={{ steering.name }} neighbor-group={{ steering.neighbor_group }} rrm=yes wnm=yes - -# [4] CONFIGURATION — main sitzt auf den Master-Radios und bringt den Channel mit -# (je Band eine configuration); alle anderen Zonen sind Slave-VAPs ohne channel=. -# steering= haengt nur an den Master-Zonen (RouterOS exportiert es nach ssid=). -{% for z in ap.zones %} -{% if z.master %} -{% for r in ap.radios %} -{% if r.key in z.bands %} -/interface wifi configuration add channel=ch-{{ r.key }} country={{ ap.country }} datapath=dp-{{ z.name }} mode=ap name=cfg-{{ z.name }}-{{ r.key }} security=sec-{{ z.name }} ssid={{ z.ssid }} steering={{ steering.name }} -{% endif %} -{% endfor %} -{% else %} -/interface wifi configuration add country={{ ap.country }} datapath=dp-{{ z.name }} mode=ap name=cfg-{{ z.name }} security=sec-{{ z.name }} ssid={{ z.ssid }} -{% endif %} -{% endfor %} +# [1] WLAN — ein CAP haelt KEINE eigene WLAN-Config. +# Bis 2026-08-18 rendert dieser Zweig Kanaele, Datapaths, Security, Configurations +# und Steering. Das war der Standalone-Stand von TP3; seit dem CAPsMAN-Cutover +# (TP28, 2026-08-17) kommen all diese Objekte vom Manager `crs326-core` und werden +# auf dem CAP als DYNAMISCHE Interfaces angelegt. Die statischen Reste auf ap1/ap2 +# waren danach wirkungslos — aber sie standen weiter in der Config und haetten, +# als Soll gerendert, totes Zeug zur Absicht erklaert (dasselbe Argument wie bei +# den defconf-Firewallregeln in TP26). Sie sind am 2026-08-18 abgeraeumt worden. +# +# Die fuenf wifi-*-ComplianceFeatures greifen jetzt am Core, s. dort [6b]. +# Hier bleibt genau eine Zeile: der CAP muss wissen, wo er suchen soll. +# ⚠️ `discovery-interfaces` ist Layer-2-Discovery — der Manager wird ueber seine +# MAC gefunden, nicht ueber eine IP. Deshalb muessen MNDP (udp/5678) und CAPWAP +# (udp/5246-5247) in der input-Kette des Core offen bleiben (TP5 Etappe 4). +/interface wifi cap set discovery-interfaces=vlan{{ ap.mgmt.vlan }} enabled=yes # [4b] BRIDGE-PORTS — ether2 fehlt bewusst: seit der Uplink-Umstellung auf ether1 (PoE) # ist er KEIN Bridge-Port mehr. ether1 traegt kein comment=defconf (beim Rebuild @@ -327,6 +372,10 @@ /interface list member add comment=defconf interface={{ ap.bridge }} list=LAN /interface list member add interface=vlan{{ ap.mgmt.vlan }} list=LAN /ip dns set allow-remote-requests=yes servers={{ ap.mgmt.gateway }} +# Zeitquelle ist der Core. Vor dem 2026-08-18 lief dort kein NTP-Server — ap2 war deshalb 17 h +# versetzt und seine Logs fuer jede geraeteuebergreifende Korrelation wertlos. +/system ntp client set enabled=yes +/system ntp client servers add address={{ ap.mgmt.gateway }} /interface vlan add interface={{ ap.bridge }} name=vlan{{ ap.mgmt.vlan }} vlan-id={{ ap.mgmt.vlan }} /ip address add address={{ obj.primary_ip4.address }} interface=vlan{{ ap.mgmt.vlan }} network={{ mgmt_host.split(".")[:3] | join(".") }}.0 /ip route add comment="mgmt default via core" dst-address=0.0.0.0/0 gateway={{ ap.mgmt.gateway }} diff --git a/routeros/tp19-ntp-dhcp-option.rsc b/routeros/tp19-ntp-dhcp-option.rsc new file mode 100644 index 0000000..450af49 --- /dev/null +++ b/routeros/tp19-ntp-dhcp-option.rsc @@ -0,0 +1,14 @@ +# NTP-Server per DHCP ausliefern (Christophs Vorschlag 2026-08-18). +# +# WARUM DAS BESSER IST ALS PRO GERAET EINZUTRAGEN: bisher kannte nur die +# MikroTik-Flotte eine Zeitquelle, und die per Hand. Ueber `ntp-server=` im +# dhcp-server network bekommt **jeder** DHCP-Client den Standort-NTP-Server +# mitgeliefert (DHCP-Option 42) — ohne dass irgendwo eine IP gepflegt werden +# muss. Das Gateway des jeweiligen VLAN ist immer .1, also traegt jedes Netz +# seinen eigenen ein. +# +# Voraussetzung ist erfuellt: der Core ist seit heute selbst `synchronized` +# (tp19-ntp-core-client.rsc) und laeuft als Server (tp19-ntp-core-als-zeitquelle.rsc, +# inkl. der input-Regel, ohne die er seit TP5 Etappe 4 unerreichbar waere). +:foreach n in=[/ip dhcp-server network find] do={ :do { /ip dhcp-server network set $n ntp-server=([/ip dhcp-server network get $n gateway]) } on-error={ :log info "tp19ntp: dhcp-option warf" } } +:log info ("tp19ntp: dhcp-netze-mit-ntp=" . [:tostr [:len [/ip dhcp-server network find where ntp-server!=""]]] . " von " . [:tostr [:len [/ip dhcp-server network find]]]) diff --git a/routeros/tp28-ap-wifi-reste-abraeumen.rsc b/routeros/tp28-ap-wifi-reste-abraeumen.rsc new file mode 100644 index 0000000..296ee63 --- /dev/null +++ b/routeros/tp28-ap-wifi-reste-abraeumen.rsc @@ -0,0 +1,34 @@ +# TP28 — die toten Standalone-WLAN-Objekte vom CAP abraeumen. +# +# BEFUND (2026-08-18): ap1 und ap2 tragen weiterhin die komplette WLAN-Config aus +# dem TP3-Standalone-Aufbau — 2 channel, 6 datapath, 6 security, 7 configuration, +# 1 steering. Seit dem CAPsMAN-Cutover (2026-08-17) kommt all das vom Manager und +# wird auf dem CAP als DYNAMISCHES Interface angelegt; die statischen Objekte sind +# wirkungslos. Sie im Template als Soll zu rendern hiesse, totes Zeug zur Absicht +# zu erklaeren — dasselbe Argument, mit dem in TP26 die defconf-Firewallregeln der +# APs nicht ins Template geschrieben wurden. +# +# ⚠️ REIHENFOLGE IST PFLICHT: `configuration` referenziert security, datapath und +# channel. Wer die Referenzierten zuerst loescht, bekommt einen Fehler und laesst +# einen Teil-Zustand zurueck (Netmiko-Lehre aus TP5 Etappe 1). +# +# 🔴 `[find !dynamic]` WIRFT HIER EINEN SYNTAXFEHLER (2026-08-18 aufgeschlagen): +# `/interface wifi configuration` und Geschwister haben gar keine `dynamic`- +# Eigenschaft -- die gibt es nur auf `/interface wifi` selbst. Der erste Versuch +# lief deshalb komplett in die on-error-Zweige und aenderte NICHTS, meldete aber +# "Plan Completed". Wieder ein Beleg: nur die Gegenpruefung am Geraet zaehlt. +# Richtig ist hier schlicht `[find]` -- diese Objekte sind ausnahmslos statisch. +# +# ⚠️ Die dynamischen VAPs (`/interface wifi find dynamic`) werden NICHT angefasst. +# Sie tragen ihre Werte inline (datapath.vlan-id=..., SSID im Kommentar) und +# referenzieren kein lokales Objekt -- am Geraet gegengeprueft. +# +# Erwartung: die Client-Verbindungen bleiben unberuehrt, weil kein einziges dieser +# Objekte von einem laufenden VAP referenziert wird. +:do { /interface wifi configuration remove [find] } on-error={ :log info "tp28c: configuration warf" } +:do { /interface wifi steering remove [find] } on-error={ :log info "tp28c: steering warf" } +:do { /interface wifi security remove [find] } on-error={ :log info "tp28c: security warf" } +:do { /interface wifi datapath remove [find] } on-error={ :log info "tp28c: datapath warf" } +:do { /interface wifi channel remove [find] } on-error={ :log info "tp28c: channel warf" } + +:log info ("tp28c: ENDE cfg=" . [:tostr [:len [/interface wifi configuration find]]] . " sec=" . [:tostr [:len [/interface wifi security find]]] . " dp=" . [:tostr [:len [/interface wifi datapath find]]] . " ch=" . [:tostr [:len [/interface wifi channel find]]] . " st=" . [:tostr [:len [/interface wifi steering find]]] . " cap-enabled=" . [:tostr [/interface wifi cap get enabled]]) diff --git a/routeros/tp7.1-core-ether13-trunk.rsc b/routeros/tp7.1-core-ether13-trunk.rsc new file mode 100644 index 0000000..ecf35af --- /dev/null +++ b/routeros/tp7.1-core-ether13-trunk.rsc @@ -0,0 +1,19 @@ +# TP7.1 — core ether13 (pve) um VLAN10 + VLAN40 erweitern. +# +# WOFUER: der SSDP-Relay fuer die macOS-Sonos-App braucht ein Bein in BEIDEN +# VLANs. SSDP ist link-lokales Multicast mit TTL 1: der M-SEARCH des MacBooks +# verlaesst VLAN10 nie, die ssdp:alive-NOTIFYs der Sonos verlassen VLAN40 nie. +# Ein Relay kann nur kopieren, was es selbst hoert — Routing hilft nicht, und +# `forward accept vlan10<->vlan40` ist laengst offen. Es scheitert VOR dem +# Verbindungsaufbau, an der Entdeckung. +# +# IST-STAND (live 2026-08-18): pve haengt an ether13, das nur `tagged vlan20` +# traegt (pvid=1). Tags hinzuzufuegen stoert VLAN20 nicht. +# +# ⚠️ tagged= wird VOLLSTAENDIG gesetzt, nicht angehaengt — die bestehenden +# Mitglieder ether1,ether8,ether15 stehen deshalb explizit mit drin. Ein +# `set tagged=ether13` allein wuerde die anderen entfernen und AP1 sowie den +# branch-Uplink aus dem VLAN werfen. +:do { /interface bridge vlan set [find vlan-ids=10 and !dynamic] tagged=ether1,ether8,ether13,ether15 } on-error={ :log info "tp71: vlan10 warf" } +:do { /interface bridge vlan set [find vlan-ids=40 and !dynamic] tagged=ether1,ether8,ether13,ether15 } on-error={ :log info "tp71: vlan40 warf" } +:log info ("tp71: ENDE v10-tagged=" . [:tostr [/interface bridge vlan get [find vlan-ids=10 and !dynamic] tagged]] . " v40-tagged=" . [:tostr [/interface bridge vlan get [find vlan-ids=40 and !dynamic] tagged]])