92/92 compliant, 0 Drifts (vorher 71/76 bei 19 Rules -- jetzt 23 Rules). NTP: die Wurzel war doppelt. Der Core hatte keinen eigenen Client, UND sein Server hielt Stratum 16 fest, weil er eingeschaltet wurde, bevor der Client synchron war. Der Test, der es aufgeklaert hat: branch einmal auf at.pool.ntp.org zeigen lassen -- 20 Sekunden bis synchronized, also war der Client heil und der Server schwieg. Jetzt im Template, per DHCP-Option 42 an alle Clients ausgeliefert und mit der Rule system-ntp ueberwacht. TP7.1: SSDP-Relay laeuft als LXC 108 auf pve, zweibeinig in VLAN10+40. User-bestaetigt: "sonos ist da!". core ether13 traegt dafuer jetzt beide VLANs. TP28: die APs trugen noch die komplette Standalone-WLAN-Config aus TP3 -- wirkungslos, aber sie haette als Soll totes Zeug zur Absicht erklaert. Abgeraeumt, ohne einen einzigen Client zu verlieren (30 vorher, 30 nachher). Die fuenf wifi-*-Features haengen jetzt am Core, dazu drei neue Rules (wifi-capsman, wifi-provisioning, wifi-cap). Gelernt: [find !dynamic] ist auf /interface wifi configuration ein SYNTAXFEHLER, kein Filter -- der erste Anlauf aenderte nichts und meldete trotzdem "Plan Completed". Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
383 lines
27 KiB
Django/Jinja
383 lines
27 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
|
|
# (ether3 ist untagged-Member von VLAN40, hat aber pvid=1; 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, 25) %}
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether{{ n }}
|
|
{% endfor %}
|
|
/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.
|
|
/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,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
|
|
/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.{{ 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
|
|
|
|
# [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
|
|
|
|
# [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.
|
|
/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="<<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
|
|
/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
|
|
/ip dns static add address=10.40.99.1 comment="crs326-core (L3)" name=core.bf.lan type=A
|
|
/ip dns static add address=10.40.99.2 comment=crs326-branch name=branch.bf.lan type=A
|
|
/ip dns static add address=10.40.99.11 comment="ap1 = reiner AP" name=ap1.bf.lan type=A
|
|
/ip dns static add address=10.40.99.12 comment="ap2 = reiner AP" name=ap2.bf.lan type=A
|
|
/ip dns static add address=10.40.20.167 name=macmini.bf.lan type=A
|
|
/ip dns static add address=10.40.20.100 name=pve.bf.lan type=A
|
|
/ip dns static add address=10.40.20.173 name=pve-mcp.bf.lan type=A
|
|
/ip dns static add address=10.40.20.10 name=nas.bf.lan type=A
|
|
/ip dns static add address=10.40.20.126 name=docker.bf.lan type=A
|
|
/ip dns static add address=10.40.20.20 name=platform-pg.bf.lan 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
|
|
{% 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
|
|
|
|
# L2-FABRIC — wie beim Core ein Literal-Block (Begruendung dort).
|
|
# ether9-23 tragen pvid=20 OHNE VLAN-Membership (Bootstrap-Rev4-Rest, funktionslos) —
|
|
# bewusst so abgebildet, damit eine spaetere Bereinigung als Drift sichtbar wird.
|
|
/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, 24) %}
|
|
/interface bridge port add bridge=bridge comment=defconf interface=ether{{ n }} pvid=20
|
|
{% endfor %}
|
|
/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 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 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
|
|
# neu angelegt), die uebrigen schon — deshalb hier nicht ueber einen Kamm geschoren.
|
|
/interface bridge port add bridge={{ ap.bridge }} comment=defconf interface=ether3
|
|
/interface bridge port add bridge={{ ap.bridge }} comment=defconf interface=ether4
|
|
/interface bridge port add bridge={{ ap.bridge }} comment=defconf interface=ether5
|
|
{% 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 — Nur Master-Radios sind Bridge-Ports (Slave-VAPs forwarden darueber),
|
|
# deshalb muessen wifi1/wifi2 tagged-Member je Client-VLAN sein. 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 }}
|
|
{% endif %}
|