geoblock: Regeln an den Chain-Anfang setzen + Reassert-Watchdog fuer gemanagte Firewalls

Problem: auf Hosts mit gemanagter Firewall (z.B. Endian-UTM) stand die DROP-Regel
am Ende der INPUT-Chain (-A) und wurde von einer ACCEPT-Regel davor abgefangen ->
Geoblocking griff nicht, obwohl Set/Config korrekt waren.

geoblock.py:
- DROP-Regel jetzt wie die LOG-Regel per 'iptables -I' an den ANFANG der Chain
  (Reihenfolge LOG -> DROP). Rule-Setzen in set_iptables_rules() refaktoriert.
- Neue Aktion --reassert: leichtgewichtiger Watchdog, der die Regel-Position
  prueft und die Regeln nur bei Bedarf wieder nach oben setzt -- OHNE Zonefiles
  von ipdeny nachzuladen (nutzt bestehendes ipset). Kein Eingriff, wenn kein
  ipset existiert (dann erst voller --apply noetig).
- geoblock_rules_on_top(): erkennt, ob die Regeln luueckenlos die ersten der
  Chain sind. --status zeigt die Position jetzt mit an.

systemd:
- geoblock-reassert.service + .timer (alle 2 Min, opt-in via install_geoblock.sh
  --with-watchdog). uninstall raeumt sie mit ab und stoppt sie vor --remove.

README: Abschnitt "Regeln bleiben nicht oben (gemanagte Firewall)" + tcpdump-
Diagnose (anonymisiert) + Fehlersuche-Abschnitt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
duffyduck
2026-07-23 11:35:38 +02:00
co-authored by Claude Opus 4.8
parent 4fce0cd7f0
commit b170759657
6 changed files with 323 additions and 32 deletions
+143
View File
@@ -18,6 +18,8 @@ und droppt Pakete bereits auf Netzwerk-Ebene.
| `geoblock.ini` | Eure eigene, lokale Konfiguration (Laenderliste, Chain, Logging, ...) — **nicht** im Repo/Git, siehe unten |
| `geoblock.service` | systemd-Unit, fuehrt `--apply` aus |
| `geoblock.timer` | systemd-Timer: beim Booten + einmal taeglich |
| `geoblock-reassert.service` | systemd-Unit, fuehrt `--reassert` aus (Watchdog, optional) |
| `geoblock-reassert.timer` | systemd-Timer: alle 2 Min pruefen ob die Regeln noch oben stehen (optional, nur bei `--with-watchdog`) |
| `install_geoblock.sh` | Installer: kopiert alles nach `/opt/geoblock`, richtet Timer ein (Testserver optional, siehe unten) |
| `geoblock_checkmk` | checkmk local-check Plugin (manuell kopieren) |
| `geoblock_testserver.py` | Kleiner Test-Webserver, um die Regeln von aussen zu pruefen |
@@ -120,10 +122,17 @@ ipset list -n | grep -i geoblock
```bash
sudo python3 geoblock.py --config geoblock.ini --apply # aktivieren/aktualisieren
sudo python3 geoblock.py --config geoblock.ini --apply --dry-run # nur simulieren
sudo python3 geoblock.py --config geoblock.ini --reassert # Regel-Position pruefen/korrigieren (ohne Download)
sudo python3 geoblock.py --config geoblock.ini --status # Zustand pruefen
sudo python3 geoblock.py --config geoblock.ini --remove # rueckgaengig
```
`--reassert` ist ein leichtgewichtiger Watchdog: es laedt **keine** Zonefiles
von ipdeny (nutzt das bestehende ipset) und setzt die Block-Regeln nur dann neu
an den Anfang der Chain, wenn sie fehlen oder nach unten gerutscht sind. Gedacht
fuer Hosts mit gemanagter Firewall — siehe Abschnitt "Regeln bleiben nicht oben"
unten.
`--apply` ist idempotent (kein doppeltes Anlegen von Regeln) und tauscht
das ipset atomar per `swap`, damit waehrend der Aktualisierung kein Loch
im Blocking entsteht.
@@ -133,6 +142,140 @@ zusaetzlich braucht: `iptables-persistent` / `netfilter-persistent save`
nutzen — der systemd-Timer sorgt aber ohnehin bei jedem Boot fuer einen
frischen `--apply`-Lauf, das reicht in der Praxis meist aus.
## Fehlersuche: "es wird nicht geblockt, obwohl konfiguriert"
Typisches Symptom: Land ist in `countries`, `--apply` lief fehlerfrei — und
trotzdem kommt die Verbindung durch. Die Ursache laesst sich in drei Schritten
eindeutig eingrenzen (jeder Schritt schliesst eine Ursache aus):
**1. Ist die IP ueberhaupt im ipset?** Das Blocking matcht gegen den *Inhalt*
des Sets, nicht gegen das Zonefile. Mit der echten oeffentlichen Quell-IP des
Test-Clients:
```bash
sudo ipset test geoblock <public-ip-des-clients>
```
- `is NOT in set` / `set geoblock does not exist` → das Set ist leer/veraltet.
Meist erreicht der Server ipdeny.com nicht (kein direkter Internetzugang →
`proxy` in der `.ini` setzen, siehe unten). Die `--apply`-Ausgabe pruefen:
dort muss `ipset 'geoblock' befuellt mit <N> Bereichen ... <LAND>` stehen.
- `is in set` → Set ist ok, weiter mit Schritt 2.
**2. Erreichen die Pakete die DROP-Regel — oder faengt sie vorher eine
ACCEPT-Regel ab?** Zaehler nullen, testen, ansehen:
```bash
sudo iptables -Z INPUT # alle Paketzaehler auf 0
# jetzt vom Client aus verbinden (z.B. check_openvpn.py, siehe unten)
sudo iptables -L INPUT -n -v --line-numbers # Zaehler ablesen
```
- **DROP-Zeile (`... match-set geoblock src ... -j DROP`) zaehlt hoch** →
Blocking greift, alles korrekt.
- **DROP-Zeile bleibt 0, aber eine LOG-Zeile weiter oben zaehlt hoch** →
klassisches **Reihenfolge-Problem**: das Paket wird von einer ACCEPT-Regel
*ueber* der DROP-Regel akzeptiert, bevor es dort ankommt. `geoblock.py` setzt
seine LOG-/DROP-Regeln zwar per `iptables -I` an den **Anfang** der Chain,
damit genau das nicht passiert — aber auf Hosts mit einer **gemanagten
Firewall** (Shorewall, firewalld, UFW, UTM-Appliances mit eigenen Sub-Chains
wie `CUSTOMINPUT`/`PROXYIN`/…) generiert die Firewall-Software die Chain bei
jedem Reload komplett neu und schiebt die extern eingefuegte Regel wieder nach
unten (oder wirft sie raus). Dann hilft entweder, das Geoblocking **im Regelwerk
der Firewall-Software selbst** einzuhaengen — oder der mitgelieferte
**Reassert-Watchdog**, der die Regeln periodisch wieder nach oben setzt (siehe
Abschnitt "Regeln bleiben nicht oben" unten).
- **Auch die LOG-Zeile bleibt 0** → das Paket erreicht diese Chain gar nicht
mit der erwarteten Quell-IP/Port. Weiter mit Schritt 3.
**3. Kommt die echte Client-IP ueberhaupt am Server an? (tcpdump)** Auf dem
Geoblock-Host mitschneiden, waehrend der Client verbindet — hier am Beispiel
OpenVPN auf `1194/udp`, Port/Proto entsprechend anpassen:
```bash
sudo tcpdump -ni any udp port 1194
# fuer einen TCP-Dienst z.B.: sudo tcpdump -ni any tcp port 443
```
Beispiel-Ausgabe (IPs anonymisiert; `203.0.113.7` = oeffentliche Client-IP,
`10.0.0.10` = private Adresse des Servers selbst):
```
10:40:34.253787 IP 203.0.113.7.33807 > 10.0.0.10.1194: UDP, length 14 # Client-Handshake kommt an
10:40:34.254013 IP 10.0.0.10.1194 > 203.0.113.7.33807: UDP, length 26 # Server ANTWORTET -> nicht geblockt
```
Kommt die **echte** Client-IP als Quelle an *und* antwortet der Server (zweite
Zeile), obwohl das Land geblockt sein sollte, ist der Paketweg sauber — dann
liegt es an der Regel-Reihenfolge (Schritt 2) oder am Set (Schritt 1), nicht am
Netzwerkpfad.
Dann vom Client aus verbinden und die **Quell-IP** (`IP <quelle>.<port> > …`)
in der Ausgabe ansehen:
- **Es erscheint die echte oeffentliche Client-IP** → Paketweg ist sauber, das
Problem liegt bei der Regel (Schritt 2, Reihenfolge) oder am Set (Schritt 1).
- **Es erscheint eine *andere* IP** (private Adresse, die eines Reverse-Proxys/
Load Balancers) → davor sitzt ein NAT/Proxy, der die Quell-IP umschreibt.
Dann kann iptables/ipset per `src` prinzipiell nicht die echte Client-IP
filtern — Geoblocking muss dann eine Ebene weiter vorne greifen (siehe die
Abschnitte "Betrieb hinter einem Reverse-Proxy" und "…AWS Load Balancer"
unten).
- **Es erscheint gar nichts** → der Traffic laeuft nicht ueber diesen Host/Port
(falscher Server, anderer Port, oder eine vorgelagerte Firewall droppt schon
vorher).
## Regeln bleiben nicht oben (gemanagte Firewall) — Reassert-Watchdog
`geoblock.py` setzt seine Block-Regeln per `iptables -I` an den **Anfang** der
Chain, damit sie vor etwaigen ACCEPT-Regeln greifen. Auf Hosts mit einer
**gemanagten Firewall** (Shorewall, firewalld, UFW, UTM-Appliances wie
Securepoint/Kerio mit eigenen Sub-Chains) reicht das aber nicht dauerhaft: Die
Firewall-Software baut die Chain bei jedem eigenen Reload komplett neu und
schiebt fremde Regeln dabei nach unten — oder entfernt sie ganz. Ab dann greift
das Geoblocking nicht mehr (siehe Fehlersuche-Abschnitt oben, "Fall Reihenfolge").
Dagegen gibt es den **Reassert-Watchdog**: ein zusaetzlicher Timer, der
regelmaessig prueft, ob die Regeln noch ganz oben stehen, und sie ggf. wieder
nach oben setzt. Der Check ist billig — er laedt **nichts** von ipdeny nach,
sondern nutzt das bestehende ipset und fasst iptables nur an, wenn wirklich
etwas verrutscht ist.
**Manuell:**
```bash
sudo python3 geoblock.py --config geoblock.ini --reassert
# --status zeigt die aktuelle Position an:
sudo python3 geoblock.py --config geoblock.ini --status
# -> "Regel-Position in Chain 'INPUT': am Anfang (ok)"
# -> oder "... NACH UNTEN GERUTSCHT -- '--reassert' oder '--apply' setzt sie wieder an den Anfang"
```
**Automatisch (systemd-Timer, alle 2 Minuten):** bei der Installation die Option
`--with-watchdog` mitgeben:
```bash
sudo ./install_geoblock.sh --with-watchdog
# ggf. kombiniert:
sudo ./install_geoblock.sh --with-testserver --with-watchdog /opt/geoblock
```
Das installiert und aktiviert `geoblock-reassert.timer` (+ `.service`). Takt
anpassen: `OnUnitActiveSec` in `/etc/systemd/system/geoblock-reassert.timer`
(z.B. `1min` bei Firewalls, die oft reloaden). Kontrolle:
```bash
systemctl list-timers geoblock-reassert.timer
journalctl -u geoblock-reassert.service -n 20
```
**Hinweis:** Der Watchdog ist ein pragmatischer Reparaturmechanismus, kein
Ersatz fuer eine saubere Integration. Wenn moeglich, ist es robuster, das
Geoblocking direkt im Regelwerk der Firewall-Software einzuhaengen — dann bleibt
die Regel-Reihenfolge auch ohne den Watchdog stabil. Ohne bestehendes ipset
(z.B. bevor je ein `--apply` lief) macht `--reassert` bewusst nichts; das
Befuellen uebernimmt der taegliche `geoblock.timer`.
## Verbindungstest (Test-Webserver)
`geoblock_testserver.py` ist ein winziger HTTP-Server ohne
+14
View File
@@ -0,0 +1,14 @@
[Unit]
Description=Geoblocking Watchdog - Block-Regeln am Anfang der Chain halten (prueft/korrigiert Position, ohne ipdeny-Download)
Documentation=https://git.hacker-net.de/Aria-Software/geoblocking-python-script
ConditionPathExists=__INSTALL_DIR__/geoblock.ini
[Service]
Type=oneshot
User=root
# --reassert laedt KEINE Zonefiles (kein Netz noetig), nutzt das bestehende ipset
# und setzt die Regeln nur dann neu an den Anfang der Chain, wenn sie nach unten
# gerutscht sind oder fehlen -- z.B. weil eine gemanagte Firewall die Chain neu
# gebaut hat. Ist noch kein ipset vorhanden, tut es nichts (der geoblock.timer
# fuellt es beim naechsten --apply).
ExecStart=/usr/bin/python3 __INSTALL_DIR__/geoblock.py --config __INSTALL_DIR__/geoblock.ini --reassert
+15
View File
@@ -0,0 +1,15 @@
[Unit]
Description=Geoblocking Watchdog - regelmaessig pruefen, ob die Block-Regeln noch am Anfang der Chain stehen
[Timer]
# Kurz nach dem Booten einmal, danach alle 2 Minuten. Der Lauf ist billig
# (nur ein paar iptables-Reads, kein ipdeny-Download) und greift nur ein, wenn
# die Regeln tatsaechlich nach unten gerutscht sind. Wer schneller/traeger will,
# passt OnUnitActiveSec an (z.B. 1min fuer Firewalls, die oft reloaden).
OnBootSec=3min
OnUnitActiveSec=2min
AccuracySec=30s
Unit=geoblock-reassert.service
[Install]
WantedBy=timers.target
+109 -30
View File
@@ -49,8 +49,12 @@ AUTOMATISCH AKTUELL HALTEN (Systemstart + taeglich):
Siehe systemd/geoblock.service + geoblock.timer sowie
install_geoblock.sh im gleichen Repo — das richtet Timer fuer
"beim Booten" + "einmal taeglich" automatisch ein.
--apply ist idempotent: es flusht das bestehende Set und befuellt es neu,
legt aber KEINE doppelte iptables-Regel an (wird vorher geprueft).
--apply ist idempotent: es flusht das bestehende Set und befuellt es neu und
entfernt vorher ALLE bestehenden geoblock-Regeln der Chain (auch verwaiste aus
frueheren Laeufen mit anderer ini), bevor es die Regeln frisch setzt. Die
DROP-/LOG-Regeln werden dabei per 'iptables -I' an den ANFANG der Chain gesetzt,
damit sie vor etwaigen ACCEPT-Regeln greifen (wichtig auf Hosts mit gemanagter
Firewall, die eigene ACCEPT-Regeln/Sub-Chains mitbringt).
PORT-EINSCHRAENKUNG (optional):
Per Default blockt das Script ALLE Ports/Protokolle fuer die gelisteten
@@ -325,6 +329,85 @@ def purge_geoblock_rules(cfg, dry_run=False):
return removed
def set_iptables_rules(cfg, dry_run=False):
"""Setzt die LOG-/DROP-Regel(n) mit -I an den ANFANG der Chain, damit sie VOR
etwaigen ACCEPT-Regeln greifen. Sonst akzeptiert eine weiter oben stehende
ACCEPT-Regel das Paket und eine ans Ende (-A) gehaengte DROP-Regel kommt nie
dran -- typisch auf Hosts mit gemanagter Firewall (Shorewall/firewalld/UFW/
UTM-Appliances mit eigenen Sub-Chains).
Setzt voraus, dass das ipset bereits existiert/befuellt ist UND die Chain
vorher per purge_geoblock_rules() bereinigt wurde (sonst entstehen Dubletten).
Pro Variante wird DROP zuerst an den Anfang gesetzt, dann LOG davor -> die
finale Reihenfolge in der Chain ist LOG vor DROP (erst loggen, dann droppen).
"""
name = cfg["ipset_name"]
set_match = ["-m", "set", "--match-set", name, "src"]
iface_opt = ["-i", cfg["interface"]] if cfg["interface"] else []
for variant in build_rule_variants(cfg):
proto_args = _variant_match_args(variant)
port_args = _variant_port_args(variant)
label = _variant_label(variant)
run(["iptables", "-I", cfg["chain"]] + iface_opt + proto_args + set_match +
port_args + ["-j", "DROP"], dry_run=dry_run)
log(f"DROP-Regel am Anfang von Chain '{cfg['chain']}' aktiv fuer Set '{name}' ({label})")
if cfg["log"]:
run(["iptables", "-I", cfg["chain"]] + iface_opt + proto_args + set_match +
port_args + ["-j", "LOG", "--log-prefix", cfg["log_prefix"] + " "], dry_run=dry_run)
log(f"LOG-Regel am Anfang von Chain '{cfg['chain']}' aktiv ({label})")
def geoblock_rules_on_top(cfg):
"""Prueft, ob die geoblock-Regeln (noch) ganz am Anfang der Chain stehen.
Gibt (on_top, present) zurueck:
present : ob ueberhaupt geoblock-Regeln in der Chain vorhanden sind
on_top : ob sie luueckenlos die ERSTEN Regeln der Chain sind (Index 0..k-1)
Ist present=True aber on_top=False, sind unsere Regeln nach unten gerutscht
(z.B. weil eine gemanagte Firewall die Chain neu gebaut und eigene Regeln
davor eingehaengt hat) -- dann sollten sie neu an den Anfang gesetzt werden.
"""
needle = f"--match-set {cfg['ipset_name']} src"
rules = _list_chain_rules(cfg["chain"]) # geordnete Liste der '-A ...'-Regeln
idx = [i for i, ln in enumerate(rules) if needle in ln]
present = bool(idx)
on_top = present and idx == list(range(len(idx)))
return on_top, present
def reassert_rules(cfg, dry_run=False):
"""Watchdog: stellt sicher, dass die geoblock-Regeln am ANFANG der Chain stehen
-- OHNE die Zonefiles neu von ipdeny zu laden (nutzt das bestehende ipset).
Gedacht fuer einen haeufigen Timer auf Hosts mit gemanagter Firewall, die die
Chain periodisch neu baut und dabei unsere Regeln nach unten schiebt oder
entfernt. Fehlt das ipset komplett, wird NICHT eingegriffen -- dann muss erst
ein voller --apply laufen, der das Set befuellt (der taegliche Timer erledigt das).
Rueckgabe: True, wenn korrigiert wurde; False, wenn nichts zu tun war.
"""
name = cfg["ipset_name"]
if not ipset_exists(name):
log(f"ipset '{name}' existiert nicht — reassert uebersprungen (erst voller --apply noetig).")
return False
on_top, present = geoblock_rules_on_top(cfg)
if on_top:
log("geoblock-Regeln stehen bereits am Anfang der Chain — nichts zu tun.")
return False
if present:
log("geoblock-Regeln nach unten gerutscht — setze sie neu an den Anfang der Chain.")
else:
log("geoblock-Regeln fehlen in der Chain — setze sie neu an den Anfang.")
purge_geoblock_rules(cfg, dry_run=dry_run) # evtl. verrutschte Reste einsammeln
set_iptables_rules(cfg, dry_run=dry_run)
return True
def apply_geoblock(cfg, dry_run=False):
name = cfg["ipset_name"]
@@ -361,33 +444,10 @@ def apply_geoblock(cfg, dry_run=False):
# 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).
set_match = ["-m", "set", "--match-set", name, "src"]
iface_opt = ["-i", cfg["interface"]] if cfg["interface"] else []
variants = build_rule_variants(cfg)
for variant in variants:
proto_args = _variant_match_args(variant)
port_args = _variant_port_args(variant)
label = _variant_label(variant)
if cfg["log"]:
log_check = (["iptables", "-C", cfg["chain"]] + iface_opt + proto_args + set_match +
port_args + ["-j", "LOG", "--log-prefix", cfg["log_prefix"] + " "])
exists = False if dry_run else subprocess.run(log_check, capture_output=True).returncode == 0
if not exists:
run(["iptables", "-I", cfg["chain"]] + iface_opt + proto_args + set_match +
port_args + ["-j", "LOG", "--log-prefix", cfg["log_prefix"] + " "], dry_run=dry_run)
else:
log(f"LOG-Regel ({label}) existiert bereits, ueberspringe")
if iptables_rule_exists(cfg["chain"], name, cfg["interface"], variant=variant, dry_run=dry_run):
log(f"DROP-Regel ({label}) existiert bereits, ueberspringe (idempotent)")
else:
run(["iptables", "-A", cfg["chain"]] + iface_opt + proto_args + set_match +
port_args + ["-j", "DROP"], dry_run=dry_run)
log(f"DROP-Regel in Chain '{cfg['chain']}' aktiv fuer Set '{name}' ({label})")
# 5) LOG-/DROP-Regel(n) frisch an den ANFANG der Chain setzen (siehe
# set_iptables_rules -- gleiche Logik nutzt auch --reassert). Der purge oben
# hat die Chain vorher von alten geoblock-Regeln befreit, daher keine Dubletten.
set_iptables_rules(cfg, dry_run=dry_run)
log("Fertig. Hinweis: Regeln sind NICHT reboot-persistent — "
"fuer Persistenz z.B. 'iptables-persistent' bzw. 'netfilter-persistent save' nutzen, "
@@ -418,6 +478,16 @@ def show_status(cfg):
for variant in variants:
active = iptables_rule_exists(cfg["chain"], name, cfg["interface"], variant=variant)
print(f"iptables DROP-Regel in Chain '{cfg['chain']}' ({_variant_label(variant)}) aktiv: {active}")
on_top, present = geoblock_rules_on_top(cfg)
if not present:
print(f"Regel-Position in Chain '{cfg['chain']}': keine geoblock-Regeln vorhanden")
elif on_top:
print(f"Regel-Position in Chain '{cfg['chain']}': am Anfang (ok)")
else:
print(f"Regel-Position in Chain '{cfg['chain']}': NACH UNTEN GERUTSCHT "
f"-- '--reassert' oder '--apply' setzt sie wieder an den Anfang")
print(f"Konfigurierte Laender: {', '.join(c.upper() for c in cfg['countries'])}")
if cfg["ports"]:
print(f"Port-Einschraenkung: {', '.join(cfg['ports'])} ({'/'.join(cfg['protocols'])})")
@@ -432,12 +502,17 @@ def main():
parser.add_argument("--config", required=True, help="Pfad zur .ini-Konfigurationsdatei")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--apply", action="store_true", help="Laender laden und Block-Regeln setzen/aktualisieren")
group.add_argument("--reassert", action="store_true",
help="Nur pruefen ob die Block-Regeln noch am Anfang der Chain stehen und sie "
"ggf. wieder nach oben setzen -- OHNE Zonefiles neu zu laden (schneller Watchdog "
"fuer gemanagte Firewalls, die die Chain neu bauen). Setzt ein bestehendes ipset voraus.")
group.add_argument("--remove", action="store_true", help="Block-Regeln und ipset wieder entfernen")
group.add_argument("--status", action="store_true", help="Aktuellen Zustand anzeigen")
parser.add_argument("--dry-run", action="store_true", help="Nur anzeigen was passieren wuerde, nichts aendern")
args = parser.parse_args()
action = "apply" if args.apply else ("remove" if args.remove else "status")
action = ("apply" if args.apply else "reassert" if args.reassert
else "remove" if args.remove else "status")
try:
cfg = load_config(args.config)
@@ -447,6 +522,10 @@ def main():
n_countries, n_entries = apply_geoblock(cfg, dry_run=args.dry_run)
log_result("OK", action, countries=n_countries, entries=n_entries,
msg=f"Geoblock aktualisiert ({n_countries} Laender, {n_entries} IP-Bereiche)")
elif args.reassert:
corrected = reassert_rules(cfg, dry_run=args.dry_run)
log_result("OK", action,
msg="Regeln neu an den Anfang gesetzt" if corrected else "Regeln bereits am Anfang, nichts zu tun")
elif args.remove:
remove_geoblock(cfg, dry_run=args.dry_run)
log_result("OK", action, msg="Geoblock-Regeln entfernt")
+36 -2
View File
@@ -7,7 +7,7 @@
# aktualisiert.
#
# Nutzung (als root, im Verzeichnis mit allen Repo-Dateien):
# sudo ./install_geoblock.sh [--with-testserver] [ZIELVERZEICHNIS]
# sudo ./install_geoblock.sh [--with-testserver] [--with-watchdog] [ZIELVERZEICHNIS]
#
# Der kleine Test-Webserver (geoblock_testserver.py +
# geoblock-testserver.service), gegen den man von aussen connecten
@@ -16,10 +16,18 @@
# in Sachen Testserver gar nichts (kein Copy, kein systemd-Unit, kein
# offener Port).
#
# Der Reassert-Watchdog (geoblock-reassert.service + .timer) prueft alle
# 2 Minuten, ob die Block-Regeln noch am Anfang der iptables-Chain stehen,
# und setzt sie ggf. wieder nach oben (ohne ipdeny-Download). Nur noetig auf
# Hosts mit einer gemanagten Firewall (Shorewall/firewalld/UFW/UTM-Appliance),
# die die Chain neu baut und dabei fremde Regeln nach unten schiebt. Wird NUR
# bei --with-watchdog installiert/aktiviert.
#
# Beispiele:
# sudo ./install_geoblock.sh
# sudo ./install_geoblock.sh --with-testserver
# sudo ./install_geoblock.sh --with-testserver /opt/geoblock
# sudo ./install_geoblock.sh --with-watchdog
# sudo ./install_geoblock.sh --with-testserver --with-watchdog /opt/geoblock
# sudo ./install_geoblock.sh /opt/geoblock
#
# Benoetigt im selben Verzeichnis:
@@ -30,6 +38,9 @@
# (nur bei --with-testserver:)
# geoblock_testserver.py
# geoblock-testserver.service
# (nur bei --with-watchdog:)
# geoblock-reassert.service
# geoblock-reassert.timer
#
# Voraussetzungen auf dem Host: python3, iptables, ipset, systemd.
@@ -43,12 +54,16 @@ fi
SRC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
WITH_TESTSERVER=0
WITH_WATCHDOG=0
INSTALL_DIR=""
for arg in "$@"; do
case "$arg" in
--with-testserver)
WITH_TESTSERVER=1
;;
--with-watchdog)
WITH_WATCHDOG=1
;;
-h|--help)
grep '^# ' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'
exit 0
@@ -65,6 +80,9 @@ REQUIRED_FILES=(geoblock.py geoblock.ini.example geoblock.service geoblock.timer
if [[ "$WITH_TESTSERVER" -eq 1 ]]; then
REQUIRED_FILES+=(geoblock_testserver.py geoblock-testserver.service)
fi
if [[ "$WITH_WATCHDOG" -eq 1 ]]; then
REQUIRED_FILES+=(geoblock-reassert.service geoblock-reassert.timer)
fi
for f in "${REQUIRED_FILES[@]}"; do
if [[ ! -f "$SRC_DIR/$f" ]]; then
@@ -114,6 +132,15 @@ else
echo "==> Test-Webserver wird NICHT installiert (Option --with-testserver nicht angegeben)."
fi
if [[ "$WITH_WATCHDOG" -eq 1 ]]; then
echo "==> --with-watchdog gesetzt: installiere Reassert-Watchdog (haelt die Regeln am Chain-Anfang)"
sed "s#__INSTALL_DIR__#$INSTALL_DIR#g" "$SRC_DIR/geoblock-reassert.service" \
> /etc/systemd/system/geoblock-reassert.service
cp "$SRC_DIR/geoblock-reassert.timer" /etc/systemd/system/geoblock-reassert.timer
else
echo "==> Reassert-Watchdog wird NICHT installiert (Option --with-watchdog nicht angegeben)."
fi
systemctl daemon-reload
systemctl enable --now geoblock.timer
@@ -121,12 +148,19 @@ if [[ "$WITH_TESTSERVER" -eq 1 ]]; then
systemctl enable --now geoblock-testserver.service
fi
if [[ "$WITH_WATCHDOG" -eq 1 ]]; then
systemctl enable --now geoblock-reassert.timer
fi
echo ""
echo "==> Fertig."
echo " Config anpassen: $INSTALL_DIR/geoblock.ini"
echo " Jetzt sofort testen: systemctl start geoblock.service && journalctl -u geoblock.service -f"
echo " Timer-Status: systemctl list-timers geoblock.timer"
echo " Naechste/letzte Läufe: systemctl status geoblock.timer"
if [[ "$WITH_WATCHDOG" -eq 1 ]]; then
echo " Watchdog-Status: systemctl list-timers geoblock-reassert.timer"
fi
echo " Log-Datei: $LOG_DIR/geoblock.log"
if [[ "$WITH_TESTSERVER" -eq 1 ]]; then
echo " Test-Webserver: systemctl status geoblock-testserver.service"
+6
View File
@@ -84,6 +84,12 @@ echo "==> Deinstalliere Geoblocking komplett ($INSTALL_DIR)"
stop_disable_rm_unit "geoblock-testserver.service"
rm -f "$INSTALL_DIR/geoblock_testserver.py"
# Reassert-Watchdog (falls installiert) zuerst stoppen, damit er nicht direkt
# nach dem --remove die Regeln neu setzt. (stop_disable_rm_unit ignoriert
# nicht vorhandene Units.)
stop_disable_rm_unit "geoblock-reassert.timer"
stop_disable_rm_unit "geoblock-reassert.service"
# iptables/ipset-Regeln sauber abbauen, SOLANGE Script+Config noch da sind.
if [[ -f "$INSTALL_DIR/geoblock.py" && -f "$INSTALL_DIR/geoblock.ini" ]]; then
echo "==> Entferne iptables/ipset-Regeln (geoblock.py --remove)"