diff --git a/docs/tp23-stufe6-bypass-sperre-runbook.md b/docs/tp23-stufe6-bypass-sperre-runbook.md index 0330ac9..72448d0 100644 --- a/docs/tp23-stufe6-bypass-sperre-runbook.md +++ b/docs/tp23-stufe6-bypass-sperre-runbook.md @@ -23,6 +23,12 @@ Das ist der letzte Baustein von TP23 und lebt in der **TP5-Firewall** (`forward` Reihenfolge-Regel (β€žerst der Ersatz, dann die Sperre"). - `dns-bypass-exempt`-Gruppe existiert auf dem Resolver-Paar (AGH-Client-Seite). +**Entwurf liegt bereit (freigegeben 2026-09-11, NOCH NICHT deployt β€” Christoph war remote):** +- Phase A (Substrat + Exempt/JIT-Accepts + log-only): `routeros/tp23-stufe6-phaseA-exempt-log.rsc` +- Phase B (reject + Auto-Revert): `routeros/tp23-stufe6-phaseB-reject.rsc` +- πŸ”΄ **Vor dem Deploy live verifizieren** (im Dateikopf markiert): (1) der Egress-Accept-Anker in + der forward-Kette, vor dem die Regeln stehen mΓΌssen; (2) die Exempt-IPs des dev-MacBooks. + **Noch zu bauen (Teil dieser Session, VOR dem Drop):** - **Firewall-Exempt** ist noch NICHT da: eine Address-List `dns-bypass-exempt` (permanent) mit dem dev-MacBook (`10.40.10.183` **und** `10.40.10.82`, dual-homed) + Agent-Toolchain, plus eine diff --git a/routeros/tp23-stufe6-phaseA-exempt-log.rsc b/routeros/tp23-stufe6-phaseA-exempt-log.rsc new file mode 100644 index 0000000..db42d65 --- /dev/null +++ b/routeros/tp23-stufe6-phaseA-exempt-log.rsc @@ -0,0 +1,59 @@ +# TP23 Stufe 6 β€” PHASE A: Substrat + Exempt/JIT-Accepts + log-only-Vorstufe. +# +# πŸ”΄πŸ”΄ NUR VOR ORT. Dieser Deploy allein bricht noch nichts (kein drop/reject), +# aber Phase B (der reject) macht .36/.37 zum DNS-SPOF ohne Fern-Rollback. +# Runbook: docs/tp23-stufe6-bypass-sperre-runbook.md. +# +# STATUS: ENTWURF, freigegeben 2026-09-11, NOCH NICHT DEPLOYT (Christoph war remote). +# +# β”Œβ”€ LIVE-VERIFY VOR DEM DEPLOY (nicht raten β€” MikroMCP war beim Schreiben tot) ─┐ +# β”‚ (1) ANKER <>: die forward-Regel, die HEUTE LAN->WAN-DNS zu β”‚ +# β”‚ 8.8.8.8:53 durchlaesst (der generische Internet-Egress-Accept). Meine β”‚ +# β”‚ Regeln muessen DAVOR stehen, sonst greift der Egress-Accept zuerst und β”‚ +# β”‚ die log-only-Regel zaehlt 0. Anker aus der Live-Kette holen: β”‚ +# β”‚ /ip firewall filter print where chain=forward β”‚ +# β”‚ und unten das place-before-Kommentar ersetzen. Faellt externer DNS heute β”‚ +# β”‚ schon in deny-by-default (TVs mit 8.8.8.8 loesen JETZT nicht auf), ist β”‚ +# β”‚ Stufe 6 gegenstandslos β€” dann Runbook neu bewerten. β”‚ +# β”‚ (2) EXEMPT-IPs: dev-MacBook ist dual-homed. Beide Beine gegen die Realitaet β”‚ +# β”‚ pruefen (en5/en0 koennen per DHCP gewechselt haben): β”‚ +# β”‚ /ip dhcp-server lease print where comment~"macbook" β”‚ +# β”‚ und mit der AGH-Seite (clients.persistent 'dns-bypass-exempt') abgleichen.β”‚ +# β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +# +# Weg (KEIN mikromcp-Direkt-Write β€” ip-firewall-filter steht unter Drift-Kontrolle): +# Backup davor: create_backup routerId=crs326-core (core-pre-tp23-s6-.backup) +# Deploy: python3 nautobot/gc-manual-plan.py --device crs326-core \ +# --commands-file routeros/tp23-stufe6-phaseA-exempt-log.rsc \ +# --change-id TP23-S6-PhaseA --deploy +# log-only als befristeter Versuch fuehren (NICHT als Versehen lesbar machen): +# python3 nautobot/gc-transaktion.py (Transaktion 'TP23-S6-would-drop-dns') +# Soll-Sync (Substrat + Accepts sind Dauerzustand -> ins intended): +# python3 nautobot/ingest-core-firewall.py --apply +# python3 nautobot/gc-configplan.py --refresh-compliance +# --------------------------------------------------------------------------- + +# 0) Substrat: erlaubte Resolver (dst), permanente Exempts (src), leere JIT-Liste. +:do { :if ([:len [/ip firewall address-list find list="dns-resolvers" address="10.40.20.36"]] = 0) do={ /ip firewall address-list add list=dns-resolvers address=10.40.20.36 comment="TP23-S6: AGH agh-bf1" } } on-error={ :log info "tp23s6A: 00a warf" } +:do { :if ([:len [/ip firewall address-list find list="dns-resolvers" address="10.40.20.37"]] = 0) do={ /ip firewall address-list add list=dns-resolvers address=10.40.20.37 comment="TP23-S6: AGH agh-bf2" } } on-error={ :log info "tp23s6A: 00b warf" } +:do { :if ([:len [/ip firewall address-list find list="dns-bypass-exempt" address="10.40.10.183"]] = 0) do={ /ip firewall address-list add list=dns-bypass-exempt address=10.40.10.183 comment="TP23-S6: dev-MacBook en5 (VERIFY)" } } on-error={ :log info "tp23s6A: 00c warf" } +:do { :if ([:len [/ip firewall address-list find list="dns-bypass-exempt" address="10.40.10.82"]] = 0) do={ /ip firewall address-list add list=dns-bypass-exempt address=10.40.10.82 comment="TP23-S6: dev-MacBook en0 (VERIFY)" } } on-error={ :log info "tp23s6A: 00d warf" } + +# 1) Exempt-Accepts. dev-MacBook + Agent-Toolchain behalten ihren DNS-Ausweg. +# MUSS vor der log-only-Regel stehen (action=accept terminiert -> Exempt-Traffic +# erreicht die Log-Regel nie und taucht im WOULD-DROP-Log korrekt NICHT auf). +:do { :if ([:len [/ip firewall filter find comment="TP23-S6: dns-exempt udp"]] = 0) do={ /ip firewall filter add chain=forward action=accept src-address-list=dns-bypass-exempt protocol=udp dst-port=53,853 comment="TP23-S6: dns-exempt udp" place-before=[/ip firewall filter find comment="<>"] } } on-error={ :log info "tp23s6A: 01a warf" } +:do { :if ([:len [/ip firewall filter find comment="TP23-S6: dns-exempt tcp"]] = 0) do={ /ip firewall filter add chain=forward action=accept src-address-list=dns-bypass-exempt protocol=tcp dst-port=53,853 comment="TP23-S6: dns-exempt tcp" place-before=[/ip firewall filter find comment="<>"] } } on-error={ :log info "tp23s6A: 01b warf" } + +# 2) JIT-Accepts. Leere, selbstverfallende Liste (Muster jit-admin, tp5-etappe4): +# /ip firewall address-list add list=jit-dns-bypass address= timeout=1h +:do { :if ([:len [/ip firewall filter find comment="TP23-S6: dns-jit udp"]] = 0) do={ /ip firewall filter add chain=forward action=accept src-address-list=jit-dns-bypass protocol=udp dst-port=53,853 comment="TP23-S6: dns-jit udp" place-before=[/ip firewall filter find comment="<>"] } } on-error={ :log info "tp23s6A: 02a warf" } +:do { :if ([:len [/ip firewall filter find comment="TP23-S6: dns-jit tcp"]] = 0) do={ /ip firewall filter add chain=forward action=accept src-address-list=jit-dns-bypass protocol=tcp dst-port=53,853 comment="TP23-S6: dns-jit tcp" place-before=[/ip firewall filter find comment="<>"] } } on-error={ :log info "tp23s6A: 02b warf" } + +# 3) log-only-Vorstufe. Loggt LAN->extern DNS, das NICHT zu einem erlaubten +# Resolver geht. dst-address-list=!dns-resolvers negiert die Liste. Mehrere +# Tage lesen (Kollektor 10.40.20.18:514), dann Phase B. NUR 53/853-Zeilen zaehlen. +:do { :if ([:len [/ip firewall filter find comment="TP23-S6: WOULD-DROP-DNS udp"]] = 0) do={ /ip firewall filter add chain=forward action=log log-prefix="TP5-WOULD-DROP-DNS" protocol=udp dst-port=53,853 dst-address-list=!dns-resolvers comment="TP23-S6: WOULD-DROP-DNS udp" place-before=[/ip firewall filter find comment="<>"] } } on-error={ :log info "tp23s6A: 03a warf" } +:do { :if ([:len [/ip firewall filter find comment="TP23-S6: WOULD-DROP-DNS tcp"]] = 0) do={ /ip firewall filter add chain=forward action=log log-prefix="TP5-WOULD-DROP-DNS" protocol=tcp dst-port=53,853 dst-address-list=!dns-resolvers comment="TP23-S6: WOULD-DROP-DNS tcp" place-before=[/ip firewall filter find comment="<>"] } } on-error={ :log info "tp23s6A: 03b warf" } + +:log info ("tp23s6A: ENDE exempt-udp=" . [:tostr [:len [/ip firewall filter find comment="TP23-S6: dns-exempt udp"]]] . " log-udp=" . [:tostr [:len [/ip firewall filter find comment="TP23-S6: WOULD-DROP-DNS udp"]]] . " resolvers=" . [:tostr [:len [/ip firewall address-list find list="dns-resolvers"]]] . " exempt=" . [:tostr [:len [/ip firewall address-list find list="dns-bypass-exempt"]]]) diff --git a/routeros/tp23-stufe6-phaseB-reject.rsc b/routeros/tp23-stufe6-phaseB-reject.rsc new file mode 100644 index 0000000..b4467ff --- /dev/null +++ b/routeros/tp23-stufe6-phaseB-reject.rsc @@ -0,0 +1,40 @@ +# TP23 Stufe 6 β€” PHASE B: der scharfe Schnitt (reject) + Auto-Revert. +# +# πŸ”΄πŸ”΄ NUR VOR ORT, mit offener Konsole. Erst NACHDEM das WOULD-DROP-DNS-Log +# (Phase A) mehrere Tage ruhig/verstanden ist und jeder externe Resolver +# entschieden wurde (Reservierung auf AGH / Exempt / bewusst brechen lassen). +# AB HIER ist .36/.37 ein DNS-SPOF ohne Fern-Rollback (Runbook, Gate). +# +# STATUS: ENTWURF, freigegeben 2026-09-11, NOCH NICHT DEPLOYT. +# +# WARUM reject STATT drop: reject icmp-port-unreachable laesst Clients schneller +# auf den naechsten (erlaubten) Resolver fallen, statt in einen Timeout zu laufen. +# +# WARUM die Log-Regel STEHEN BLEIBT: action=log terminiert NICHT β€” das Paket wird +# geloggt und faellt dann in die reject-Regel darunter. Gleiche Doktrin wie +# tp5-etappe3-drop (Log VOR Drop), der Kollektor zeigt weiter, was reject frisst. +# +# Weg (wie Phase A, ueber Nautobot GC): +# Deploy: python3 nautobot/gc-manual-plan.py --device crs326-core \ +# --commands-file routeros/tp23-stufe6-phaseB-reject.rsc \ +# --change-id TP23-S6-PhaseB --deploy +# Danach (reject ist jetzt Dauerzustand -> Soll, log-only-Transaktion schliessen): +# python3 nautobot/ingest-core-firewall.py --apply +# python3 nautobot/gc-configplan.py --refresh-compliance # Ziel gruen +# +# Rollback sofort (Vor-Ort-Konsole): /ip firewall filter set [find comment~"TP23-S6: reject"] disabled=yes +# -> Clients koennen sofort wieder externe Resolver nutzen. Bzw. Auto-Revert unten. +# --------------------------------------------------------------------------- + +# 0) Auto-Revert ZUERST β€” deaktiviert die reject-Regeln nach 10m, falls nicht +# bestaetigt (analog tp5-etappe3/4). Nach der Abnahme abbestellen: +# /system scheduler remove [find name="tp23s6autorevert"] +:do { :if ([:len [/system scheduler find name="tp23s6autorevert"]] = 0) do={ /system scheduler add name="tp23s6autorevert" interval=10m on-event="/ip firewall filter set [find comment~\"TP23-S6: reject\"] disabled=yes; /system scheduler remove [find name=\"tp23s6autorevert\"]; :log warning \"tp23s6: AUTO-REVERT hat die DNS-reject-Regeln abgeschaltet\"" } } on-error={ :log info "tp23s6B: 00 scheduler warf" } + +# 1) reject. Gleiche Match-Tupel wie die WOULD-DROP-DNS-Log-Regeln, direkt +# dahinter (place-before denselben Egress-Anker -> landet nach den Log-Regeln). +# <>: derselbe live-verifizierte Anker wie in Phase A. +:do { :if ([:len [/ip firewall filter find comment="TP23-S6: reject dns udp"]] = 0) do={ /ip firewall filter add chain=forward action=reject reject-with=icmp-port-unreachable protocol=udp dst-port=53,853 dst-address-list=!dns-resolvers comment="TP23-S6: reject dns udp" place-before=[/ip firewall filter find comment="<>"] } } on-error={ :log info "tp23s6B: 01a warf" } +:do { :if ([:len [/ip firewall filter find comment="TP23-S6: reject dns tcp"]] = 0) do={ /ip firewall filter add chain=forward action=reject reject-with=icmp-port-unreachable protocol=tcp dst-port=53,853 dst-address-list=!dns-resolvers comment="TP23-S6: reject dns tcp" place-before=[/ip firewall filter find comment="<>"] } } on-error={ :log info "tp23s6B: 01b warf" } + +:log info ("tp23s6B: ENDE reject-udp=" . [:tostr [:len [/ip firewall filter find comment="TP23-S6: reject dns udp"]]] . " reject-tcp=" . [:tostr [:len [/ip firewall filter find comment="TP23-S6: reject dns tcp"]]] . " autorevert=" . [:tostr [:len [/system scheduler find name="tp23s6autorevert"]]])