geoblock: eigene Block-IPs/CIDRs zusaetzlich zu den Laendern (block_ips)

Neue Option 'block_ips': komma-getrennte Liste eigener IPv4-IPs/CIDRs, die
zusaetzlich zu den ipdeny-Laender-Ranges ins selbe ipset kommen (z.B. bekannte
Angreifer, einzelne Hosts/Netze). Werden von denselben iptables-Regeln/Ports
erfasst.

- load_config validiert + normalisiert jede Angabe (ip_network strict=False,
  Host -> /32); IPv6 wird mit klarer Meldung abgelehnt (ipset ist IPv4).
- apply_geoblock nimmt sie nach den Laender-CIDRs mit ins Set auf; Fuell-Log und
  --status weisen die Anzahl/Liste aus.
- Doku: geoblock.ini.example (Kommentar + block_ips-Zeile) + README-Optionsliste.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
duffyduck
2026-07-28 10:37:40 +02:00
co-authored by Claude Opus 4.8
parent 161d90de45
commit 830e9a61f6
3 changed files with 39 additions and 2 deletions
+25 -2
View File
@@ -184,6 +184,19 @@ def load_config(path):
# 'ports' die oeffentlichen Ports (z.B. 1194, 443) eintragen, immun gegen Remap.
match_orig_dport = sec.getboolean("match_original_dport", fallback=False)
# block_ips: optionale, komma-getrennte Liste EIGENER IPs/CIDRs, die zusaetzlich
# zu den Laender-Ranges ins selbe ipset kommen (z.B. bekannte Angreifer, einzelne
# Hosts/Netze). Nur IPv4 (das ipset ist IPv4). Wird hier validiert + normalisiert.
block_ips = []
for entry in (e.strip() for e in sec.get("block_ips", "").split(",") if e.strip()):
try:
net = ipaddress.ip_network(entry, strict=False) # akzeptiert Host (->/32) und CIDR
except ValueError:
raise ValueError(f"Ungueltige IP/CIDR in 'block_ips': '{entry}'")
if net.version != 4:
raise ValueError(f"'block_ips': nur IPv4 unterstuetzt (ipset ist IPv4), ungueltig: '{entry}'")
block_ips.append(str(net))
return {
"countries": countries,
"chains": chains,
@@ -195,6 +208,7 @@ def load_config(path):
"ports": ports,
"protocols": protocols,
"match_orig_dport": match_orig_dport,
"block_ips": block_ips,
"proxy": sec.get("proxy", "").strip(),
}
@@ -480,6 +494,12 @@ def apply_geoblock(cfg, dry_run=False):
if not all_cidrs and not dry_run:
raise RuntimeError("Keine IP-Bereiche geladen — Abbruch, um kein leeres Set zu aktivieren")
# 2b) eigene Block-IPs/CIDRs aus der .ini zusaetzlich aufnehmen (bereits in
# load_config validiert/normalisiert). Kommen ins selbe Set wie die Laender.
n_block_ips = len(cfg.get("block_ips", []))
if n_block_ips:
all_cidrs.extend(cfg["block_ips"])
# 3) Set atomar neu befuellen: temp-Set bauen, dann swap (kein Traffic-Loch)
tmp_name = f"{name}_tmp"
run(["ipset", "create", tmp_name, "hash:net", "-exist"], dry_run=dry_run)
@@ -490,8 +510,9 @@ def apply_geoblock(cfg, dry_run=False):
run(["ipset", "swap", tmp_name, name], dry_run=dry_run)
run(["ipset", "destroy", tmp_name], dry_run=dry_run, check=False)
log(f"ipset '{name}' befuellt mit {len(all_cidrs)} Bereichen "
f"aus Laendern: {', '.join(c.upper() for c in cfg['countries'])}")
extra = f" + {n_block_ips} eigene(n) Block-IP(s)/CIDR(s)" if n_block_ips else ""
log(f"ipset '{name}' befuellt mit {len(all_cidrs)} Eintraegen "
f"(Laender: {', '.join(c.upper() for c in cfg['countries'])}{extra})")
# 4) Vor dem Neu-Setzen ALLE bestehenden geoblock-Regeln der Chain entfernen
# (auch verwaiste aus frueheren Laeufen mit anderer ini, z.B. andere Ports).
@@ -545,6 +566,8 @@ def show_status(cfg):
f"-- '--reassert' oder '--apply' setzt sie wieder an den Anfang")
print(f"Konfigurierte Laender: {', '.join(c.upper() for c in cfg['countries'])}")
if cfg.get("block_ips"):
print(f"Eigene Block-IPs/CIDRs ({len(cfg['block_ips'])}): {', '.join(cfg['block_ips'])}")
if cfg["ports"]:
modus = "Original-Zielport vor DNAT (conntrack)" if cfg.get("match_orig_dport") \
else "Zielport (dports)"