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:
co-authored by
Claude Opus 4.8
parent
4fce0cd7f0
commit
b170759657
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user