Bis heute gab es nur ip-firewall-nat. "72/72 compliant" sagte ueber die
Firewall gar nichts — und TP5 Etappe 3 wollte dort einen Deny-by-default
setzen, der dann als einzige Regel im Netz unbeobachtet gewesen waere.
Umsetzung datengetrieben statt als Templatezeilen: die 36 Regeln liegen in
local_config_context_data.firewall_filter, das Template rendert sie nur.
36 Regeln fest ins Jinja zu schreiben hiesse, dass jede TP5-Etappe einen
Template-Edit braucht — genau die Reibung, wegen der die Governance in
Verruf geraten ist. Neue Regel = ein Listeneintrag.
ingest-core-firewall.py ist ein RE-BASELINE-Werkzeug, kein Sync: es erklaert
den Ist-Stand zum Soll und macht blind ausgefuehrt die Drift-Erkennung
wertlos. Darum nur nach einer bewusst verifizierten Aenderung laufen lassen.
Drei Formatfallen beim Nachbau des /export-Formats, alle durch Vergleich mit
dem echten Geraete-Export bzw. durch die neue Rule selbst gefunden:
- REST liefert Booleans als STRINGS. `if rule.get("dynamic")` ist fuer
"false" wahr und filterte erst alle 36 Regeln weg (0 statt 36).
- REST sagt hw-offload=true, /export schreibt yes. Und "!" bzw. Kommas
werden NICHT gequotet (connection-nat-state=!dstnat, dst-port=8123,1883).
Quoting nur bei Leerzeichen.
- /export laesst Default-Werte weg: `disabled=no` steht nie drin,
`disabled=yes` dagegen schon. Das schlug beim ersten Lauf der neuen Rule
als 75/76 auf — die Rule hat ihren Zweck damit sofort erfuellt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Nautobot-Tooling
ingest-baseline.py
Idempotenter Basisdaten-Ingest (Locations, Rollen, Device-Types, Devices, VLANs/Prefixes, ap1-Interfaces + IPs, WLAN-Modelle). Bildet den Ist-Zustand ab, nicht den Zielzustand.
export NAUTOBOT_TOKEN=... # aus docs/nas-vlan20/stacks/210-nautobot.yml (gitignored)
python3 nautobot/ingest-baseline.py # Dry-Run
python3 nautobot/ingest-baseline.py --apply # schreiben
API-Fallen (teuer gelernt)
tagged_vlansliest sich per REST alsnull, obwohl korrekt gespeichert. Verifikation nur über GraphQL:POST /api/graphql/mit{interfaces(device:"ap1"){name mode untagged_vlan{vid} tagged_vlans{vid}}}.- Relationen kommen ohne
?depth=1ohnedisplay→ ein voll gepflegtes Device sieht leer aus. LocationType.content_typesmussipam.vlangroup/ipam.vlan/ipam.prefix/dcim.controllerenthalten, sonst 400 beim Anlegen.parent_interfacenur für virtuelle Interfaces — WLAN-VAPs können ihren Master nicht als Parent tragen.WirelessNetworkhat kein VLAN-Feld; die VLAN-Bindung hängt anwireless/controller-managed-device-group-wireless-network-assignmentsund braucht zwingend einenController(bei uns logisch,controller_deviceleer — kein CAPsMAN).RadioProfile.channel_widthwill Integer ([20,40,80]), nicht"20mhz".- IP↔Interface läuft über
/ipam/ip-address-to-interface/.