Die vier Core-Forward-Regeln fuer den Resolver waren nachgezogen, die DHCP-Antwort an die Clients noch nicht: /ip dhcp-server network trug weiterhin dns-server=10.40.20.36,10.40.20.37. Ein Client haette nach dem AGH-Umzug den Resolver unter der alten Adresse gesucht. Template geaendert, nach bf-golden-config gepusht (Git-DataSource ist die Wahrheit, nicht die lokale Datei), Git-Sync, am Core gesetzt (alle 7 Netze), per DHCPOFFER-Probe auf vmbr0.20 bestaetigt (Domain Name Server: 10.40.20.30, 10.40.20.31), Compliance 140/140 ohne Drift. Randbefund (nicht in diesem Commit): die 7 DHCP-Pools starten live bei .100 und ueberlappen die Schema-Baender .100-.199 — pool-v30 reicht sogar bis .250, wo statisch der Sonoff Dongle-M sitzt. Entscheid 2026-09-21: nur pool-v20 und pool-v30 werden auf .200-.249 verengt, v20 zuerst. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
592 lines
43 KiB
Django/Jinja
592 lines
43 KiB
Django/Jinja
# Nautobot Golden Config -- intended RouterOS config for {{ obj.name }}
|
|
#
|
|
# FORMAT-REGEL (TP22, 2026-08-13): Zeilen sind im RouterOS-**/export-Format** geschrieben
|
|
# (Space-Form `/ip address add ...`, Attribute ALPHABETISCH), nicht in der Slash-Form der
|
|
# Provisionierungs-Skripte. Golden Config vergleicht Backup (= echter /export) und Intended
|
|
# textuell -- jede Abweichung in Reihenfolge, Quoting oder Pfadform ist ein Falsch-Drift.
|
|
# Referenz-Exporte: routeros/state-backups/*-gc-backup-scrubbed-*.cfg
|
|
#
|
|
# Secrets tragen die Maske "<<REDACTED>>" der ConfigReplace-Backup-Regeln.
|
|
# 🔴 Zeilen mit <<REDACTED>> duerfen NIE per Deploy gepusht werden.
|
|
|
|
/system identity set name={{ obj.name }}
|
|
|
|
# MGMT-DIENSTE + ADDRESS-ACL (TP16.1, 2026-08-17) — fuer ALLE vier Geraete identisch.
|
|
# Warum die Mesh-Range dazu MUSS: seit TP16.1 kommt der Remote-Zugriff aus 100.96.0.0/12
|
|
# (Cloudflare Mesh). Eine ACL, die nur 10.40.0.0/16 kennt, laesst Ping durch, sperrt aber
|
|
# SSH/REST/MikroMCP aus — das Symptom hat auf dem Core eine halbe Stunde gekostet.
|
|
# Warum ftp/telnet/api-ssl aus: Klartext-Protokolle bzw. ungenutzt. Der Core hatte das
|
|
# bereits, branch/ap1/ap2 fuhren sie offen und OHNE jede Address-Beschraenkung.
|
|
# winbox war auf ALLEN vier unbeschraenkt.
|
|
# ⚠️ Lockout-Regel: 10.40.0.0/16 (LAN) UND 100.96.0.0/12 (Mesh) muessen beide drin sein —
|
|
# das sind die zwei Wege, ueber die dieses Netz administriert wird. Nie einen davon kappen.
|
|
# ⚠️ Attribut heisst `available-from`, NICHT `address` (korrigiert 2026-08-18): RouterOS
|
|
# 7.23/7.24 exportiert `available-from=`, das Template schrieb `address=`. Ergebnis waren
|
|
# vier Dauer-Drifts (ap1/ap2/branch/core), bei denen das GERAET recht hatte und das Soll
|
|
# falsch war. Beim Push haette `address=` auf 7.24 zudem auflaufen koennen.
|
|
/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
|
|
|
|
{% if obj.name == "crs326-core" %}
|
|
{#- VLAN-Tabelle: Site-Konstante. name = RouterOS-comment, lease/pool je VLAN. -#}
|
|
{%- set vlans = [
|
|
{"id": 10, "name": "MAIN", "pool": "10.40.10.100-10.40.10.200", "lease": "1d"},
|
|
{"id": 20, "name": "INFRA", "pool": "10.40.20.100-10.40.20.200", "lease": "1d"},
|
|
{"id": 30, "name": "IOT", "pool": "10.40.30.100-10.40.30.250", "lease": "1d"},
|
|
{"id": 40, "name": "MEDIA", "pool": "10.40.40.100-10.40.40.200", "lease": "1d"},
|
|
{"id": 50, "name": "UNTRUSTED", "pool": "10.40.50.100-10.40.50.200", "lease": "1d"},
|
|
{"id": 90, "name": "GUEST", "pool": "10.40.90.100-10.40.90.200", "lease": "12h"},
|
|
{"id": 99, "name": "MGMT", "pool": "10.40.99.100-10.40.99.200", "lease": "1d"}
|
|
] %}
|
|
# =====================================================================================
|
|
# crs326-core — PRODUKTIVER L3-Router (seit Cutover 2026-08-08, TP21.1).
|
|
# Die frueheren `disabled=yes`-Staging-Flags sind ENTFERNT: sie stammten aus der
|
|
# Vorbereitung des Cutovers und beschreiben nicht mehr den Sollzustand.
|
|
# =====================================================================================
|
|
|
|
# [0] L2-FABRIC — Bridge-Ports + Bridge-VLAN-Tabelle.
|
|
# Literal-Block, KEINE Ableitung aus DCIM: der As-built traegt bewusste Schiefstaende
|
|
# (ether2 haengt zusaetzlich in VLAN1). Wuerde man pvid aus `untagged_vlan` generieren,
|
|
# meldete die Compliance genau diese akzeptierten Altlasten als Dauerdrift. Der Block ist
|
|
# damit das, was Golden Config fuer einen handgebauten L2-Layer leisten soll:
|
|
# Drift-Erkennung gegen den Ist-Zustand.
|
|
# `/interface bridge` selbst ist bewusst NICHT abgedeckt (traegt die geraetespezifische
|
|
# admin-mac -> waere pro Geraet verschieden und ist kein Fleet-Standard).
|
|
/interface bridge port add bridge=bridge comment=defconf frame-types=admit-only-vlan-tagged interface=ether1
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether2 pvid=40
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether3
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether4 pvid=40
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether5 pvid=40
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether6 pvid=40
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether7
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether8
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether9
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether10
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether11 pvid=10
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether12
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether13
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether14 pvid=30
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether15
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether16 pvid=100
|
|
{% for n in range(17, 23) %}
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether{{ n }}
|
|
{% endfor %}
|
|
/interface bridge port add bridge=bridge comment=TP38-OOB-MGMT frame-types=admit-only-untagged-and-priority-tagged interface=ether23 pvid=99
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether24
|
|
/interface bridge port add bridge=bridge comment=defconf interface=sfp-sfpplus1
|
|
/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
|
|
# 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.
|
|
# ether21 = ap3 (cAP ax, Technik UG) seit 2026-08-30, TP28-Nachtrag: gleicher AP-Trunk
|
|
# wie ether15/ap1. Der Port traegt pvid=1 + frame-types=admit-all wie ether15 -- das
|
|
# VLAN1-untagged-Bein entsteht dynamisch aus dem pvid und steht deshalb nicht hier.
|
|
# ether3 = ap1 (hAP ax2) seit 2026-09-03, TP25-Sideboard-Test: ap1 testweise am Sideboard,
|
|
# Uplink von ether15 auf ether3 verlegt. Gleicher AP-Trunk wie ether15.
|
|
/interface bridge vlan add bridge=bridge tagged=ether1,ether3,ether8,ether13,ether15,ether21 vlan-ids=10
|
|
/interface bridge vlan add bridge=bridge tagged=ether3,ether8,ether13,ether15,ether21 vlan-ids=20
|
|
/interface bridge vlan add bridge=bridge tagged=ether1,ether3,ether8,ether15,ether21 untagged=ether14 vlan-ids=30
|
|
/interface bridge vlan add bridge=bridge tagged=ether1,ether3,ether8,ether13,ether15,ether21 untagged=ether2,ether4,ether5 vlan-ids=40
|
|
/interface bridge vlan add bridge=bridge tagged=ether1,ether3,ether8,ether15,ether21 vlan-ids=50
|
|
/interface bridge vlan add bridge=bridge tagged=ether1,ether3,ether8,ether15,ether21 vlan-ids=90
|
|
/interface bridge vlan add bridge=bridge tagged=ether3,ether8,ether15,ether21,bridge untagged=ether23 vlan-ids=99
|
|
/interface bridge vlan add bridge=bridge tagged=ether1 untagged=ether16 vlan-ids=100
|
|
|
|
# [1] INTERFACE-LISTS
|
|
/interface list add comment=TP15 name=WAN
|
|
/interface list add comment=TP15 name=LAN
|
|
/interface list member add interface=vlan100 list=WAN
|
|
{% for v in vlans %}
|
|
/interface list member add interface=vlan{{ v.id }} list=LAN
|
|
{% endfor %}
|
|
|
|
# [2] VLAN-SVIs auf der Bridge (vlan99 traegt bewusst keinen comment)
|
|
{% for v in vlans %}
|
|
/interface vlan add {% if v.id != 99 %}comment={{ v.name }} {% endif %}interface=bridge name=vlan{{ v.id }} vlan-id={{ v.id }}
|
|
{% endfor %}
|
|
/interface vlan add comment=WAN interface=bridge name=vlan100 vlan-id=100
|
|
|
|
# [3] GW-IPs je VLAN — datengetrieben aus Nautobot (IP am SVI-Interface gebunden).
|
|
# comment = Interface-Description; network wird aus der Host-IP abgeleitet.
|
|
{% for iface in obj.interfaces.all() %}
|
|
{% if iface.name.startswith("vlan") %}
|
|
{% for ip in iface.ip_addresses.all() %}
|
|
/ip address add address={{ ip.address }} comment="{{ iface.description }}" interface={{ iface.name }} network={{ ip.host.split(".")[:3] | join(".") }}.0
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
# [4] WAN — DHCP-Client auf vlan100 (Liwest/Arris)
|
|
/ip dhcp-client add comment="WAN Arris DHCP (Liwest) via vlan100" interface=vlan100 use-peer-dns=no use-peer-ntp=no
|
|
|
|
# [5] DHCP-Server je VLAN
|
|
{% for v in vlans %}
|
|
/ip pool add name=pool-v{{ v.id }} ranges={{ v.pool }}
|
|
{% endfor %}
|
|
{% for v in vlans %}
|
|
/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.20.30,10.40.20.31 domain=bf.emsenhuber.net 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
|
|
|
|
# [6] DNS — DoH zum Cloudflare-Gateway, MIT Zertifikatspruefung (TP23 Stufe 1,
|
|
# scharf seit 2026-08-18; schliesst TP21.1 TODO 1).
|
|
# `servers=1.1.1.1` BLEIBT als Bootstrap stehen: RouterOS loest den
|
|
# DoH-Hostnamen selbst darueber auf, sonst Henne-Ei. Alles andere geht
|
|
# ueber DoH.
|
|
# Die frueher hier notierte Begruendung "core hat kein CA-Bundle" war zu
|
|
# pessimistisch: es braucht kein Bundle, sondern genau EINEN Root. Die Kette
|
|
# ist Leaf -> GTS WE1 -> GTS Root R4, importiert ist nur R4 (~1 KB, gueltig
|
|
# bis 2036) — entscheidend bei 2,8 MB freiem Flash.
|
|
# Der Import selbst steht bewusst NICHT im Template: er braucht die Datei
|
|
# auf dem Geraet und ist ein einmaliger Bootstrap, kein Sollzustand.
|
|
# 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
|
|
|
|
# [6a2] LOGGING — Beweissicherung, kein Betriebsdetail. TP25 Fund E, 2026-08-30.
|
|
#
|
|
# Bis dahin liefen `wireless`-Logs NUR nach `action=memory`; nur `firewall` ging in den
|
|
# Kollektor. Der Ringpuffer fasste 75 Zeilen und war von einer Stunde Arbeit fast
|
|
# ueberschrieben -- ein seit Wochen gemeldeter iPhone-Abbruch war rueckwirkend deshalb
|
|
# nicht diagnostizierbar. Die zwei neuen Zeilen sind die Datengrundlage fuer die
|
|
# Entscheidung ueber die Steering-Hebel (2g-probe-delay / ft-over-ds / signal-range).
|
|
#
|
|
# ⚠️ Der Topic heisst `wireless`, NICHT `wifi` -- auch auf dem neuen wifi-qcom-Stack
|
|
# (am Geraet nachgelesen). Ein geratenes `wifi` waere wirkungslos geblieben.
|
|
# ⚠️ RouterOS UND-verknuepft Topics innerhalb EINER Regel (`info,!firewall` = info UND
|
|
# NICHT firewall). `topics=wireless,caps` haette NICHTS gematcht -> zwei Regeln.
|
|
# 📌 Nur der Core traegt hier Zeilen: RouterOS exportiert Defaults nicht, und unter
|
|
# CAPsMAN fuehrt ohnehin der MANAGER die Roaming-Events -- die CAPs loggen lokal nichts.
|
|
# Auf ap1/ap2/ap3/branch sind Soll und Ist beide leer, also compliant.
|
|
/system logging action add name=tp5syslog remote=10.40.20.18 target=remote
|
|
/system logging set 0 topics=info,!firewall
|
|
/system logging add action=tp5syslog topics=firewall
|
|
/system logging add action=tp5syslog comment="TP25 Fund E: WLAN-Roaming/Disconnects dauerhaft in den Kollektor" topics=wireless
|
|
/system logging add action=tp5syslog comment="TP25 Fund E: CAP-An-/Abmeldungen dauerhaft in den Kollektor" topics=caps
|
|
|
|
# [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
|
|
# "<<REDACTED>>", 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.
|
|
# ap1/ap2 auf Band B (DFS) seit dem TP25-Kanalplan-Umzug (committed 2026-09-04). Frequenz-LISTE
|
|
# statt Einzelwert: bei einer Radar-Erkennung muss das Radio auf einen anderen Wert der Liste
|
|
# ausweichen koennen -- ein Einzelwert waere ein SPOF. ap1=52+56 (5260/Ce), ap2=60+64 (5300/Ce).
|
|
/interface wifi channel add band=5ghz-ax frequency=5260,5280 name=ch-5g-ap1 width=20/40mhz
|
|
/interface wifi channel add band=5ghz-ax frequency=5300,5320 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
|
|
/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
|
|
# sec-main: ft=yes/ft-over-ds=yes zurueckgesetzt am 2026-09-14 (Ruecknahme des 09-04-Entscheids
|
|
# "ft=no als Dauerzustand"; Go nach Terrasse-In-situ-Session: mit ft=no war jeder Roam eine
|
|
# Voll-Reassoziation -- darin starben Anrufe). Known-Good: genau dieser Stand lief in der
|
|
# Fund-E-Abschlussmessung 09-02 ruhig (4 reauth/8,6 h, 0 Ausfaelle) auf 7.24.1.
|
|
# 2026-09-04 stand hier: ft=no ft-over-ds=no (User-Entscheid nach TP25-Messung).
|
|
/interface wifi security add authentication-types=wpa3-psk ft=yes ft-over-ds=yes name=sec-main passphrase="<<REDACTED>>"
|
|
/interface wifi security add authentication-types=wpa2-psk,wpa3-psk disabled=no ft=no name=sec-infra passphrase="<<REDACTED>>"
|
|
/interface wifi security add authentication-types=wpa2-psk,wpa3-psk disabled=no ft=no name=sec-iot passphrase="<<REDACTED>>"
|
|
/interface wifi security add authentication-types=wpa2-psk,wpa3-psk ft=no name=sec-media passphrase="<<REDACTED>>"
|
|
/interface wifi security add authentication-types=wpa2-psk,wpa3-psk disabled=no ft=no name=sec-untrusted passphrase="<<REDACTED>>"
|
|
/interface wifi security add authentication-types=wpa2-psk,wpa3-psk disabled=no ft=no name=sec-guest passphrase="<<REDACTED>>"
|
|
/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
|
|
# st-main-5g (TP25, 2026-09-15): transition-threshold=-72 statt Default -80 -- 5G bricht frueher
|
|
# zur 2.4G ab (Terrasse sass am Rundgang 09-15 auf der -80/5G-Kante). transition-threshold-time=5s
|
|
# statt 10s. NUR 5G: die drei 2G-Configs bleiben auf st-main (unset=-80), weil 2.4G draussen
|
|
# (Terrasse/ausserhalb der Wohnung) Reichweite behalten soll. 8 dB Abstand = kleine Hysterese.
|
|
/interface wifi steering add name=st-main-5g neighbor-group=bf-main rrm=yes transition-threshold=-72 transition-threshold-time=5s 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-5g
|
|
/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-5g
|
|
/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-5g
|
|
/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
|
|
# ap1 ist ans Sideboard umgezogen (TP25-Kanalplan committed 2026-09-04) und hat wieder eine
|
|
# Provisioning-Zeile: Band B `5260/Ce` (5G) + ch1 (2,4G). Die frueher bewusst weggelassenen
|
|
# Zeilen (2026-08-30, ap1 stand neben ap3 auf 5180 und haette kollidiert) sind damit
|
|
# gegenstandslos. Reihenfolge wie am Geraet: ap2, ap3, ap1.
|
|
/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
|
|
/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
|
|
|
|
# [6c] WLAN ACCESS-LIST — der Soll-Zustand ist LEER, und das ist eine Aussage.
|
|
#
|
|
# Bis 2026-08-22 stand dieser Abschnitt unter KEINER ComplianceRule. Aenderungen
|
|
# dort erzeugten keinen Drift und liessen sich nicht als Transaktion fuehren --
|
|
# `gc-transaktion.py` verlangt eine ComplianceRule als --scope. Das war nicht
|
|
# theoretisch: `docs/tp25-pin-s22.sh` schreibt bei jedem Messrundgang
|
|
# access-list-Regeln (Kommentar TP25-PIN-S22) auf den Manager, um ein Telefon auf
|
|
# einen AP festzunageln.
|
|
#
|
|
# Seit der ComplianceRule `wifi-access-list` faellt jeder Eintrag hier auf.
|
|
# Waehrend eines Messlaufs ist der gemeldete Drift ERWARTET -- dann gehoert er als
|
|
# offene Transaktion gefuehrt:
|
|
# python3 nautobot/gc-transaktion.py begin --device crs326-core \
|
|
# --scope wifi-access-list --change-id TEST-... --expires JJJJ-MM-TT
|
|
#
|
|
# 🔴 Stand 2026-09-14 (Terrasse-In-situ-Session): der `signal-range`-Boden gegen
|
|
# klebende Clients ist GEPRUEFT und GESCHEITERT -- `/interface wifi access-list`
|
|
# hat kein `band`-Property (Band-Scoping ginge nur ueber cap-wifiN-Interface-Namen,
|
|
# die bei jedem CAP-Rejoin wechseln), SSID-/MAC-Scope ohne Band träfe 2,4 GHz mit
|
|
# (Lockout-Risiko, Schlafzimmer -84). Der Boden ist damit NICHT umsetzbar; der
|
|
# Ersatz gegen die Call-Aussetzer ist `ft=yes ft-over-ds=yes` auf sec-main
|
|
# (2026-09-14 zurueckgesetzt, s. docs/transaktionen/TP25-ft-yes-secmain.commands.rsc
|
|
# und handoff-20260914-tp25-ap2-insitu-terrassse.md): Roams laufen als nahtlose
|
|
# FT-Transition statt Voll-Reassoziation. Das Band-Springen selbst endet erst
|
|
# mit dem Deckungsfix (TP25 Variante 3). Siehe teilprojekte.md TP25 (Stand 09-14).
|
|
|
|
# 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="crs326-core (L3)" name=core.bf.emsenhuber.net type=A
|
|
/ip dns static add address=10.40.99.2 comment=crs326-branch name=branch.bf.emsenhuber.net type=A
|
|
/ip dns static add address=10.40.99.11 comment="ap1 = reiner AP" name=ap1.bf.emsenhuber.net type=A
|
|
/ip dns static add address=10.40.99.12 comment="ap2 = reiner AP" name=ap2.bf.emsenhuber.net type=A
|
|
/ip dns static add address=10.40.20.167 name=macmini.bf.emsenhuber.net type=A
|
|
/ip dns static add address=10.40.20.100 name=pve.bf.emsenhuber.net type=A
|
|
/ip dns static add address=10.40.20.173 name=pve-mcp.bf.emsenhuber.net type=A
|
|
/ip dns static add address=10.40.20.10 name=nas.bf.emsenhuber.net type=A
|
|
/ip dns static add address=10.40.20.126 name=docker.bf.emsenhuber.net type=A
|
|
/ip dns static add address=10.40.20.20 name=platform-pg.bf.emsenhuber.net type=A
|
|
|
|
# [7] NAT
|
|
/ip firewall nat add action=accept chain=srcnat comment="TP4: no-NAT inter-VLAN (LAN-internal, before masquerade)" dst-address=10.40.0.0/16
|
|
/ip firewall nat add action=masquerade chain=srcnat comment="defconf: masquerade" ipsec-policy=out,none out-interface-list=WAN
|
|
|
|
# [7b] FIREWALL FILTER (TP26, 2026-08-17). Kommt als DATEN aus
|
|
# local_config_context_data.firewall_filter, nicht als Templatezeilen —
|
|
# 36 Regeln fest hier einzutragen hiesse, dass jede TP5-Etappe einen
|
|
# Template-Edit braucht. Befuellt/re-baselined von
|
|
# nautobot/ingest-core-firewall.py (bewusst manuell, nie automatisch:
|
|
# ein Soll, das dem Ist nachlaeuft, erkennt keine Drift mehr).
|
|
# Reihenfolge ist semantisch — die ComplianceRule steht auf config_ordered.
|
|
{% for line in obj.get_config_context().get("firewall_filter", []) %}
|
|
/ip firewall filter {{ line }}
|
|
{% endfor %}
|
|
|
|
# [8] ROUTEN zu den WG-Spokes (Default-Route kommt dynamisch vom WAN-DHCP-Client)
|
|
/ip route add comment="TP10: FM spoke -> wg-hub" dst-address=10.101.0.0/24 gateway=wg-hub
|
|
/ip route add comment="TP10: DW spoke -> wg-hub" dst-address=10.95.10.0/24 gateway=wg-hub
|
|
|
|
# [9] WG-HUB (TP10 Multi-Site). Der echte Privkey liegt in routeros/ap1-wg-hub.privkey.
|
|
/interface wireguard add comment="TP10 Multi-Site VPN Hub (FM/DW spokes)" listen-port=51821 mtu=1412 name=wg-hub private-key="<<REDACTED>>"
|
|
/interface wireguard peers add allowed-address=10.101.0.0/24 comment=fm endpoint-address=abxhutfgsid9snmy.myfritz.net endpoint-port=52476 interface=wg-hub name=fm public-key="dzRY0lnnUygijChWe5wXZmXPeNcbdALpqBptA3fyiVE="
|
|
/interface wireguard peers add allowed-address=10.95.10.0/24 comment=dw interface=wg-hub name=dw public-key="FqYZLztQ/qME5VDie5QLGH++5299WJiE1dPpQnPbzUw="
|
|
|
|
# [10] CF-MESH (TP16.1) — privater Zugang unterwegs via natives WireGuard zum CF-PoP.
|
|
# Ersetzt den cloudflared-QUIC-Proxy fuer 10.40.0.0/16 (die oeffentlichen FQDNs bleiben
|
|
# auf dem 3-Tunnel-HA aus TP16). Privkey kommt von CLOUDFLARE, nicht vom Geraet — er
|
|
# steht in mcp/cloudflare/.env (CF_MESH_PRIVKEY, gitignored) und wurde einmalig per SSH
|
|
# eingespielt; hier nur die Backup-Maske, wie bei wg-hub.
|
|
# MTU 1420 aus dem animmouse-Guide. Vergleichswert auf demselben Geraet: wg-hub = 1412.
|
|
# Bei Path-MTU-Symptomen (TCP baut auf, grosse Transfers haengen) zuerst dorthin angleichen.
|
|
# Peer wurde von RouterOS zunaechst "peer5" auto-benannt und auf "cf-pop" umbenannt.
|
|
# ⚠️ MikroMCPs manage_wireguard_peer kennt weder persistent-keepalive noch einen Namen,
|
|
# und es schreibt endpoint="IP:Port" komplett in endpoint-address (endpoint-port bleibt 0
|
|
# -> KEIN Handshake). Nach jedem Peer-Add per `print detail` gegenlesen.
|
|
/interface wireguard add comment="TP16.1 Cloudflare Mesh" listen-port=13279 mtu=1420 name=cf-mesh private-key="<<REDACTED>>"
|
|
/interface wireguard peers add allowed-address=0.0.0.0/0 comment="TP16.1 CF-PoP" endpoint-address=162.159.193.2 endpoint-port=2408 interface=cf-mesh name=cf-pop persistent-keepalive=25s public-key="bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo="
|
|
/ip address add address=100.96.0.6 comment="TP16.1 Mesh-Overlay" interface=cf-mesh network=100.96.0.6
|
|
/ip route add comment="TP16.1 Mesh" disabled=no distance=1 dst-address=100.96.0.0/12 gateway=cf-mesh
|
|
|
|
# [11] QoS — BEWUSST LEER (TP6 geschlossen 2026-08-23).
|
|
#
|
|
# Hier stand einen Tag lang eine CAKE-Queue. Sie ist entfernt, weil sie GEMESSEN
|
|
# nichts gebracht hat — und das ist die Aussage dieses Abschnitts:
|
|
#
|
|
# ohne CAKE 42,4 ms | 88 % 43,3 | 73 % 44,0 | 52 % der Linerate 44,6 (idle 12,9)
|
|
#
|
|
# Selbst bei halber Linerate keine Verbesserung. Dort MUSS die Warteschlange im Router
|
|
# liegen — also entsteht die Zusatzlatenz stromabwaerts, im Arris/DOCSIS-Uplink
|
|
# (Request/Grant-Scheduling auf MAC-Layer, gar keine Queue). Kein Shaper hier erreicht das.
|
|
#
|
|
# 🔴 Zwei Dinge, die beim naechsten QoS-Gedanken Zeit sparen:
|
|
# 1. `/queue tree parent=<interface>` ist auf diesem Geraet WIRKUNGSLOS — MikroTik:
|
|
# "Interface HTB will not work correctly when the out-interface is hardware offloaded
|
|
# and the bridge Fast Path is not active." Der Weg ist `parent=global` + ein
|
|
# `mangle chain=postrouting out-interface=vlan100 action=mark-packet`. So gebaut
|
|
# zaehlt und drosselt es nachweislich (9,3 -> 7,78 Mbit bei cake-bandwidth=7M).
|
|
# 2. Eine Simple Queue auf `10.40.0.0/16` taugt hinter NAT NICHT als Probe: src-nat
|
|
# laeuft VOR der Simple Queue, das Target matcht nie.
|
|
# Merkregeln: Memory `ref_queue_htb_platzierung_docsis`.
|
|
#
|
|
# ⚖️ Was hier sehr wohl ginge und offen ist: nicht Bufferbloat bekaempfen, sondern
|
|
# Saettigung VERMEIDEN (Bulk-Uploads zuruecknehmen, damit ein Call gar nicht erst in
|
|
# die Saettigung geraet). Das ist ein anderes Ziel und steht als TP6.1.
|
|
# Die ComplianceRule `queue` bewacht diesen Abschnitt weiter — jetzt mit dem Inhalt aus [11a].
|
|
|
|
# [11a] UPLOAD-PRIORISIERUNG (TP6.1, 2026-08-23) — das, was TP6 NICHT konnte.
|
|
#
|
|
# Der Unterschied in einem Satz: TP6 wollte das Warten ertraeglicher machen und
|
|
# scheiterte am DOCSIS-Puffer. Das hier verhindert, dass ueberhaupt gewartet wird.
|
|
# Die 42 ms treten NUR unter Saettigung auf — im Leerlauf misst dieselbe Strecke
|
|
# 12,9 ms. Ein NAS-Sync, der bei Last zurueckweicht, haelt die Leitung da raus.
|
|
#
|
|
# 🔴 PRIORITAET, KEIN CAP. Ein festes Limit fuer die NAS wuerde sie auch nachts
|
|
# bremsen, wenn die Leitung leer ist — genau die ungenutzte Bandbreite, die
|
|
# Christoph zu Recht nicht will (DSM drosselt von sich aus nicht). HTB nimmt dem
|
|
# Bulk die Bandbreite nur, solange jemand anders sie braucht.
|
|
#
|
|
# 🔴 `src-address=10.40.20.10` — die NAS-HOST-IP, bewusst NICHT ganz VLAN20.
|
|
# In INFRA haengen auch HA, Nautobot und pve; die gehoeren nicht pauschal nach
|
|
# hinten. Wer das je auf 10.40.20.0/24 erweitert, verlangsamt seine Werkzeuge.
|
|
#
|
|
# 🔴 `parent=global`, NICHT `parent=vlan100`. Interface-HTB ist auf diesem Geraet
|
|
# wirkungslos (hw-offloadetes out-interface, s. [11]). Der Global-Weg ist in TP6
|
|
# als wirksam BEWIESEN: 9,3 -> 7,78 Mbit bei gesetztem Limit.
|
|
#
|
|
# ⚠️ Die Markierung MUSS im postrouting stehen und die NAS-Regel VOR dem Catch-all:
|
|
# mangle postrouting laeuft VOR src-nat, nur dort ist die LAN-Quelle noch sichtbar
|
|
# (nach dem Masquerade ist alles die WAN-IP). Deshalb steht `ip-firewall-mangle`
|
|
# auf `config_ordered` — eine vertauschte Reihenfolge waere STILL wirkungslos.
|
|
#
|
|
# max-limit=9M auf dem Parent: knapp unter der gemessenen Linerate von 9,6 Mbit.
|
|
# HTB kann nur verteilen, was es selbst begrenzt — ohne dieses Limit gibt es keine
|
|
# Knappheit zu priorisieren. Preis: ~0,6 Mbit Spitze.
|
|
# limit-at sind Garantien, keine Grenzen: der Sync verhungert nie ganz (1M), und
|
|
# der Rest hat 6M sicher, auch wenn der Sync laeuft.
|
|
/ip firewall mangle add action=mark-packet chain=postrouting comment="TP6.1: NAS-Bulk" new-packet-mark=bulkup out-interface=vlan100 passthrough=no src-address=10.40.20.10
|
|
/ip firewall mangle add action=mark-packet chain=postrouting comment="TP6.1: Rest (interaktiv)" new-packet-mark=restup out-interface=vlan100 passthrough=no
|
|
/queue tree add max-limit=9M name=wan-up-total parent=global
|
|
/queue tree add limit-at=6M max-limit=9M name=wan-up-rest packet-mark=restup parent=wan-up-total priority=1
|
|
# ⚠️ Bei wan-up-bulk steht KEIN priority=8, obwohl genau das gemeint ist: 8 ist der
|
|
# Default, und /export laesst Defaults weg. Ein explizites priority=8 im Template
|
|
# erzeugt deshalb einen Dauer-Drift, bei dem das Geraet recht hat.
|
|
/queue tree add limit-at=1M max-limit=9M name=wan-up-bulk packet-mark=bulkup parent=wan-up-total
|
|
{% elif obj.role.name == "Switch" %}
|
|
# =====================================================================================
|
|
# L2-Switch (crs326-branch) — Sollzustand des Mgmt-Layers.
|
|
# Bewusst NICHT emittiert: die TEMP-Bring-up-Objekte (vlan10 "TEMP Mgmt VLAN10" +
|
|
# 10.40.10.2). Die tauchen dadurch als Compliance-`extra` auf — genau richtig, denn sie
|
|
# sind Altlast aus dem Bring-up und gehoeren entfernt (VLAN1/VLAN10-Interim, TP19).
|
|
# =====================================================================================
|
|
/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
|
|
|
|
# INPUT-FIREWALL (TP38, 2026-09-15) — deny-by-default fuer die Mgmt-Ebene (10.40.99.2).
|
|
# Der Branch ist L2-only: ALLE Mgmt-Anfragen kommen auf der einen vlan99-SVI an, egal ob
|
|
# VLAN10 (ueber den Core geroutet), cf-mesh (100.96.0.0/12) oder der OOB-Port (untagged
|
|
# ether23, 10.40.99.x). Deshalb matcht die Quelle per src-address, nicht per in-interface.
|
|
# Der src-Rahmen 10.40.0.0/16 + 100.96.0.0/12 spiegelt bewusst das `available-from` der
|
|
# /ip service-Zeilen — kein neues Fenster, nur stateful + deny-by-default + WOULD-DROP-Log.
|
|
{% for line in obj.get_config_context().get("firewall_filter", []) %}
|
|
/ip firewall filter {{ line }}
|
|
{% endfor %}
|
|
|
|
# L2-FABRIC — wie beim Core ein Literal-Block (Begruendung dort).
|
|
# ether9-22 tragen pvid=20 OHNE VLAN-Membership (Bootstrap-Rev4-Rest, funktionslos) —
|
|
# bewusst so abgebildet, damit eine spaetere Bereinigung als Drift sichtbar wird.
|
|
# ether23 ist seit TP38 der OOB-MGMT-Port (untagged VLAN99), s. unten.
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether1
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether2
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether3 pvid=10
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether4 pvid=10
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether5 pvid=40
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether6 pvid=40
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether7 pvid=20
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether8
|
|
{% for n in range(9, 23) %}
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether{{ n }} pvid=20
|
|
{% endfor %}
|
|
/interface bridge port add bridge=bridge comment=TP38-OOB-MGMT frame-types=admit-only-untagged-and-priority-tagged interface=ether23 pvid=99
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether24
|
|
/interface bridge port add bridge=bridge comment=defconf disabled=yes interface=sfp-sfpplus1
|
|
/interface bridge port add bridge=bridge comment=defconf disabled=yes interface=sfp-sfpplus2
|
|
/interface bridge vlan add bridge=bridge comment=MAIN tagged=ether1,ether24,bridge untagged=ether3,ether4 vlan-ids=10
|
|
/interface bridge vlan add bridge=bridge comment=INFRA tagged=ether1,ether24,bridge,ether2,ether8 untagged=ether7 vlan-ids=20
|
|
/interface bridge vlan add bridge=bridge comment=IOT tagged=ether1,ether24,bridge vlan-ids=30
|
|
/interface bridge vlan add bridge=bridge comment=MEDIA tagged=ether1,ether24,bridge untagged=ether5,ether6 vlan-ids=40
|
|
/interface bridge vlan add bridge=bridge comment=UNTRUSTED tagged=ether1,ether24,bridge vlan-ids=50
|
|
/interface bridge vlan add bridge=bridge comment=GUEST tagged=ether1,ether24,bridge vlan-ids=90
|
|
/interface bridge vlan add bridge=bridge comment=MGMT tagged=ether1,ether24,bridge untagged=ether23 vlan-ids=99
|
|
{% elif obj.role.name == "Wireless AP" %}
|
|
{% set ap = obj.get_config_context()["ap"] %}
|
|
{% set mgmt_host = obj.primary_ip4.host %}
|
|
{% set uplink = ap.uplink %}
|
|
# =====================================================================================
|
|
# TP22 — Standalone-AP (kein CAPsMAN). Gerendert aus ConfigContext "ap-common"
|
|
# + Device.local_config_context_data (Kanaele) + DCIM (Name, primary_ip4).
|
|
#
|
|
# SCOPE: nur die COMPLIANCE-VERWALTETEN Sektionen, bewusst NICHT die ganze Config.
|
|
# Golden Config vergleicht per ComplianceRule.match_config sektionsweise -> defconf-
|
|
# Boilerplate (ipv4/ipv6-Firewall, /disk, /tool mac-server) und die VAP-Zeilen
|
|
# (/interface wifi add ... mac-address=...) bleiben draussen: deren MACs sind
|
|
# geraetevergeben und in Nautobot nicht modelliert.
|
|
#
|
|
# 🔴 NIE als Ganzes auf ein laufendes Geraet pushen: die Provisionierung setzt
|
|
# zwischenzeitlich vlan-filtering=no, was den vlan99-Mgmt-Pfad abreisst -> der Rest
|
|
# der Zeilen kaeme ueber dieselbe SSH-Session nie an. Voll-Reprovisionierung
|
|
# ausschliesslich per Datei-Upload + /import (siehe routeros/.secrets/ap*-prov-base.rsc).
|
|
#
|
|
# Attributreihenfolge = alphabetisch, exakt wie RouterOS /export sie schreibt.
|
|
# Referenz: routeros/state-backups/ap1-gc-backup-scrubbed-20260813.cfg
|
|
# =====================================================================================
|
|
|
|
# [1] WLAN — ein CAP haelt keine eigenen SSIDs, aber sehr wohl den lokalen Anschluss.
|
|
# 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 bleiben zwei Zeilen: wo der CAP seinen Manager suchen soll, und WOHIN er
|
|
# die vom Manager erzeugten Slave-VAPs haengt.
|
|
# ⚠️ `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).
|
|
#
|
|
# 🔴 `slaves-datapath` IST NICHT OPTIONAL — Ausfall 2026-08-18, rund 14 h IOT+MEDIA:
|
|
# Der Manager schickt `datapath.bridge=bridge` zwar mit, der CAP uebernimmt davon
|
|
# aber NUR die vlan-id — die Bridge-Mitgliedschaft ist und bleibt lokal. Die
|
|
# Master-Radios wifi1/wifi2 sind statische Bridge-Ports (s. [4b]) und liefen
|
|
# deshalb weiter; die dynamischen Slave-VAPs (iot/media/untrusted/guest) haengen
|
|
# dagegen in GAR KEINER Bridge, solange `slaves-datapath` auf kein lokales
|
|
# datapath-Objekt zeigt. Beim Cutover (2026-08-17) wurde das Feld nie gesetzt —
|
|
# der Ausfall beginnt dort, nicht bei der TP5-Haertung und nicht beim Abraeumen
|
|
# der Standalone-Reste am 2026-08-18 (das hat ihn nur unreparierbar gemacht).
|
|
# Symptom: Client assoziiert sauber (am Manager als Registration sichtbar!),
|
|
# bekommt aber nie DHCP; am Core kommt auf dem VLAN kein einziges Frame an —
|
|
# `torch` leer UND Drop-Log leer, es wird also nichts verworfen, es kommt nichts.
|
|
# Das Objekt traegt bewusst KEINE vlan-id: die liefert der Manager pro VAP.
|
|
/interface wifi cap set discovery-interfaces=vlan{{ ap.mgmt.vlan }} enabled=yes slaves-datapath=dp-caps-slaves
|
|
# ⚠️ Export-Reihenfolge != Deploy-Reihenfolge: RouterOS exportiert `cap` VOR `datapath`,
|
|
# beim Push muss das datapath-Objekt aber ZUERST existieren.
|
|
/interface wifi datapath add bridge={{ ap.bridge }} comment="TP28-fix: bridge fuer CAPsMAN-Slave-VAPs" name=dp-caps-slaves
|
|
|
|
# [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
|
|
# neu angelegt), die uebrigen schon — deshalb hier nicht ueber einen Kamm geschoren.
|
|
# Die Liste kommt seit 2026-08-30 aus `ap.extra_ports` statt hartkodiert: ap1/ap2
|
|
# sind hAP ax2 mit ether3/4/5, ap3 ist ein cAP ax mit nur zwei Ports (Liste leer).
|
|
# Die alte Hartkodierung war die stille Annahme "AP == hAP ax2".
|
|
{% for p in ap.extra_ports %}
|
|
/interface bridge port add bridge={{ ap.bridge }} comment=defconf interface={{ p }}
|
|
{% endfor %}
|
|
{% for r in ap.radios %}
|
|
/interface bridge port add bridge={{ ap.bridge }} comment=defconf interface={{ r.iface }}
|
|
{% endfor %}
|
|
/interface bridge port add bridge={{ ap.bridge }} interface={{ uplink }}
|
|
|
|
# [5] BRIDGE-VLAN — wifi1/wifi2 sind tagged-Member je Client-VLAN. Die Begruendung von
|
|
# 2026-08-17 ("Slave-VAPs forwarden ueber den Master") war falsch: mit gesetztem
|
|
# `slaves-datapath` haengt jeder Slave-VAP als eigener DYNAMISCHER Bridge-Port mit
|
|
# pvid = seiner Manager-vlan-id in der Bridge. Die tagged-Zeilen bleiben trotzdem
|
|
# stehen — sie kosten nichts und tragen den Uplink-Trunk. Die frueheren
|
|
# "Ausnahmen" fallen hier aus den Daten heraus: IOT hat bands=["2g"] -> nur wifi2.
|
|
/interface bridge vlan add bridge={{ ap.bridge }} untagged={{ ap.bridge }},{{ uplink }} vlan-ids={{ ap.native_vlan }}
|
|
{% for z in ap.zones %}
|
|
{% set members = [uplink] + (ap.radios | selectattr("key", "in", z.bands) | map(attribute="iface") | list) %}
|
|
/interface bridge vlan add bridge={{ ap.bridge }} tagged={{ members | join(",") }} vlan-ids={{ z.vlan }}
|
|
{% endfor %}
|
|
# MGMT liegt auf der Bridge, NICHT auf den Radios - sonst waere Management over-the-air.
|
|
/interface bridge vlan add bridge={{ ap.bridge }} tagged={{ uplink }},{{ ap.bridge }} vlan-ids={{ ap.mgmt.vlan }}
|
|
|
|
# [6] MGMT-LAYER
|
|
# interface-list + dns hier, damit die platform-weiten ComplianceFeatures
|
|
# (gelten pro Platform, nicht pro Rolle) auf APs nicht ins Leere laufen.
|
|
/interface list add comment=defconf name=WAN
|
|
/interface list add comment=defconf name=LAN
|
|
/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 }}
|
|
|
|
{% if ap.container %}
|
|
# [7] CONTAINER-NETZ des cloudflared-Connectors (TP16.3 §5, ins Soll gehoben 2026-08-31)
|
|
# Bis hierher standen diese Zeilen per DIREKT-WRITE am Geraet (Runbook §3.4) und
|
|
# erzeugten je AP zwei extra-Drifts: Runbook und GC-Governance widersprachen sich,
|
|
# /ip address und /ip firewall stehen unter Drift-Kontrolle. Sie gehoeren ins Soll
|
|
# und NICHT in eine gc-transaktion: eine Transaktion ist ein befristeter Versuch,
|
|
# das hier ein Dauerzustand.
|
|
#
|
|
# 🔴 Das masquerade ist Pflicht, nicht Kosmetik. Der Container spricht aus 172.17.0.2;
|
|
# dem Core ist 172.17.0.0/24 unbekannt, es gibt weder Route noch Rueckweg. Erst
|
|
# hinter dem masquerade kommt der Verkehr als vlan99 an und wird von der Freigabe
|
|
# "TP31: AP-cloudflared -> caddy-bf1" (vlan99 -> vlan20:443) getragen.
|
|
#
|
|
# 🔑 <<REDACTED>> steht hier NICHT fuer ein Geheimnis, sondern fuer zwei
|
|
# geraetevergebene MACs, die auf jedem AP anders lauten und in Nautobot bewusst
|
|
# nicht modelliert sind (gleiche Entscheidung wie bei den VAP-MACs, s. [1]).
|
|
# Die ConfigReplace-Regeln 60-/61-veth-* normalisieren sie im Backup
|
|
# (nautobot/ingest-veth-rule.py). Zweiter, willkommener Effekt: die Maske ist die
|
|
# Push-Sperre aus TP22-Regel 1 -- eine Zeile mac-address=<<...>> darf nie aufs Geraet.
|
|
#
|
|
# Ein kuenftiger AP ohne Connector setzt "container": {} per local_config_context_data;
|
|
# dann rendert dieser Block nichts und die Regel interface-veth vergleicht leer gegen leer.
|
|
{% set c = ap.container %}
|
|
/interface veth add address={{ c.veth_address }} container-mac-address=<<REDACTED>> dhcp=no gateway={{ c.gateway }} gateway6="" mac-address=<<REDACTED>> name={{ c.veth }}
|
|
/ip address add address={{ c.ap_address }} interface={{ c.veth }} network={{ c.network }}
|
|
/ip firewall nat add action=masquerade chain=srcnat comment="{{ c.nat_comment }}" src-address={{ c.subnet }}
|
|
{% endif %}
|
|
{% endif %}
|