geoblock.py: vor Apply/Remove alle bestehenden geoblock-Regeln der Chain purgen
Neue Funktion purge_geoblock_rules() entfernt per 'iptables -S <chain>' ALLE Regeln mit '--match-set <name> src' (LOG wie DROP), unabhaengig von Proto/Port/Variante. Das Loeschkommando wird per shlex direkt aus der -S-Ausgabe rekonstruiert, damit --log-prefix inkl. Leerzeichen exakt passt. - --apply purged jetzt vor dem Neu-Setzen der Regeln, aber erst NACH dem atomaren Befuellen des ipsets -> kein Traffic-Loch waehrend des ipdeny-Downloads. - --remove nutzt denselben Purge statt der bisherigen varianten-basierten Loeschung -> keine verwaisten Regeln mehr, wenn sich ports/protocol zwischen zwei Laeufen geaendert haben. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b5ff887cea
commit
4fce0cd7f0
+50
-25
@@ -65,6 +65,7 @@ import argparse
|
|||||||
import configparser
|
import configparser
|
||||||
import datetime
|
import datetime
|
||||||
import ipaddress
|
import ipaddress
|
||||||
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
@@ -286,6 +287,44 @@ def iptables_rule_exists(chain, ipset_name, interface, variant=None, dry_run=Fal
|
|||||||
return result.returncode == 0
|
return result.returncode == 0
|
||||||
|
|
||||||
|
|
||||||
|
def _list_chain_rules(chain):
|
||||||
|
"""Gibt die aktuellen Regeln einer iptables-Chain als '-A ...'-Zeilen zurueck
|
||||||
|
(Format von 'iptables -S <chain>', eine Zeile pro Regel)."""
|
||||||
|
result = subprocess.run(["iptables", "-S", chain], capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
return []
|
||||||
|
return [ln for ln in result.stdout.splitlines() if ln.startswith("-A ")]
|
||||||
|
|
||||||
|
|
||||||
|
def purge_geoblock_rules(cfg, dry_run=False):
|
||||||
|
"""Loescht ALLE Regeln in der Chain, die auf das geoblock-ipset verweisen
|
||||||
|
(--match-set <name> src) -- unabhaengig von Proto/Port/Variante.
|
||||||
|
|
||||||
|
Dadurch werden auch alte Regeln aus frueheren --apply-Laeufen mit anderer
|
||||||
|
ini (z.B. andere Ports/Protokolle) sauber entfernt, die build_rule_variants(cfg)
|
||||||
|
fuer die AKTUELLE Config gar nicht mehr kennt. Wir rekonstruieren das Loesch-
|
||||||
|
Kommando direkt aus 'iptables -S' (per shlex, damit z.B. das --log-prefix mit
|
||||||
|
Leerzeichen erhalten bleibt), statt die Args selbst nachzubauen -- so passt das
|
||||||
|
-D immer exakt zur real existierenden Regel.
|
||||||
|
"""
|
||||||
|
name = cfg["ipset_name"]
|
||||||
|
chain = cfg["chain"]
|
||||||
|
needle = f"--match-set {name} src"
|
||||||
|
removed = 0
|
||||||
|
for line in _list_chain_rules(chain):
|
||||||
|
if needle not in line:
|
||||||
|
continue
|
||||||
|
tokens = shlex.split(line) # z.B. ["-A", "INPUT", "-m", "set", ...]
|
||||||
|
# "-A <chain> <rest>" -> "iptables -D <chain> <rest>"
|
||||||
|
run(["iptables", "-D"] + tokens[1:], dry_run=dry_run, check=False)
|
||||||
|
removed += 1
|
||||||
|
if removed:
|
||||||
|
log(f"{removed} bestehende geoblock-Regel(n) aus Chain '{chain}' entfernt (Set '{name}')")
|
||||||
|
else:
|
||||||
|
log(f"Keine bestehenden geoblock-Regeln in Chain '{chain}' gefunden")
|
||||||
|
return removed
|
||||||
|
|
||||||
|
|
||||||
def apply_geoblock(cfg, dry_run=False):
|
def apply_geoblock(cfg, dry_run=False):
|
||||||
name = cfg["ipset_name"]
|
name = cfg["ipset_name"]
|
||||||
|
|
||||||
@@ -316,7 +355,13 @@ def apply_geoblock(cfg, dry_run=False):
|
|||||||
log(f"ipset '{name}' befuellt mit {len(all_cidrs)} Bereichen "
|
log(f"ipset '{name}' befuellt mit {len(all_cidrs)} Bereichen "
|
||||||
f"aus Laendern: {', '.join(c.upper() for c in cfg['countries'])}")
|
f"aus Laendern: {', '.join(c.upper() for c in cfg['countries'])}")
|
||||||
|
|
||||||
# 4) optionale LOG-Regel(n) + DROP-Regel(n) in iptables, nur wenn noch nicht vorhanden.
|
# 4) Vor dem Neu-Setzen ALLE bestehenden geoblock-Regeln der Chain entfernen
|
||||||
|
# (auch verwaiste aus frueheren Laeufen mit anderer ini, z.B. andere Ports).
|
||||||
|
# Erst JETZT (nach dem Befuellen des Sets), damit waehrend des ipdeny-Downloads
|
||||||
|
# der bestehende Block aktiv bleibt und kein Traffic-Loch entsteht.
|
||||||
|
purge_geoblock_rules(cfg, dry_run=dry_run)
|
||||||
|
|
||||||
|
# 5) LOG-Regel(n) + DROP-Regel(n) frisch setzen.
|
||||||
# Eine Variante pro Protokoll wenn 'ports' gesetzt ist, sonst eine Variante (alle Ports).
|
# Eine Variante pro Protokoll wenn 'ports' gesetzt ist, sonst eine Variante (alle Ports).
|
||||||
set_match = ["-m", "set", "--match-set", name, "src"]
|
set_match = ["-m", "set", "--match-set", name, "src"]
|
||||||
iface_opt = ["-i", cfg["interface"]] if cfg["interface"] else []
|
iface_opt = ["-i", cfg["interface"]] if cfg["interface"] else []
|
||||||
@@ -353,30 +398,10 @@ def apply_geoblock(cfg, dry_run=False):
|
|||||||
|
|
||||||
def remove_geoblock(cfg, dry_run=False):
|
def remove_geoblock(cfg, dry_run=False):
|
||||||
name = cfg["ipset_name"]
|
name = cfg["ipset_name"]
|
||||||
iface_opt = ["-i", cfg["interface"]] if cfg["interface"] else []
|
# Loescht ALLE geoblock-Regeln der Chain (auch verwaiste aus frueheren Laeufen
|
||||||
set_match = ["-m", "set", "--match-set", name, "src"]
|
# mit anderer ini), nicht nur die aktuell konfigurierten Varianten -- so bleiben
|
||||||
# Entfernt fuer alle aktuell konfigurierten Varianten. Falls die .ini seit dem letzten
|
# keine Reste stehen, egal wie sich 'ports'/'protocol' zwischenzeitlich geaendert haben.
|
||||||
# --apply geaendert wurde (z.B. ports entfernt/hinzugefuegt), koennen dadurch alte
|
purge_geoblock_rules(cfg, dry_run=dry_run)
|
||||||
# Regeln mit anderer Variante stehen bleiben -- im Zweifel per 'iptables -L <chain> -n'
|
|
||||||
# pruefen und manuell aufraeumen.
|
|
||||||
for variant in build_rule_variants(cfg):
|
|
||||||
proto_args = _variant_match_args(variant)
|
|
||||||
port_args = _variant_port_args(variant)
|
|
||||||
|
|
||||||
for _ in range(5):
|
|
||||||
result = run(["iptables", "-D", cfg["chain"]] + iface_opt + proto_args + set_match +
|
|
||||||
port_args + ["-j", "DROP"], dry_run=dry_run, check=False)
|
|
||||||
if dry_run or result is None or result.returncode != 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
if cfg["log"]:
|
|
||||||
for _ in range(5):
|
|
||||||
result = run(["iptables", "-D", cfg["chain"]] + iface_opt + proto_args + set_match +
|
|
||||||
port_args + ["-j", "LOG", "--log-prefix", cfg["log_prefix"] + " "],
|
|
||||||
dry_run=dry_run, check=False)
|
|
||||||
if dry_run or result is None or result.returncode != 0:
|
|
||||||
break
|
|
||||||
|
|
||||||
run(["ipset", "destroy", name], dry_run=dry_run, check=False)
|
run(["ipset", "destroy", name], dry_run=dry_run, check=False)
|
||||||
log(f"Geoblock-Regeln entfernt, ipset '{name}' geloescht.")
|
log(f"Geoblock-Regeln entfernt, ipset '{name}' geloescht.")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user