# Geoblocking Blockt Verbindungen aus bestimmten Laendern auf Kernel-Ebene (iptables + ipset). Die IP-Bereiche je Land kommen kostenlos (kein API-Key) von [ipdeny.com](https://www.ipdeny.com/ipblocks/). Kein Live-GeoIP-Lookup pro Verbindung (langsam, meist kostenpflichtig) — stattdessen der Standardansatz von fail2ban/CSF: die komplette IP-Range- Liste jedes Landes liegt im Kernel (`ipset`), `iptables` matched dagegen und droppt Pakete bereits auf Netzwerk-Ebene. ## Dateien | Datei | Zweck | |---|---| | `geoblock.py` | Hauptscript: laedt Laender-IPs, setzt/entfernt ipset+iptables-Regeln | | `geoblock.ini.example` | Beispiel-Konfiguration (Vorlage im Repo, siehe unten) | | `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 | | `geoblock-testserver.service` | systemd-Unit, startet den Test-Webserver dauerhaft | | `uninstall_geoblock.sh` | Deinstaller: raeumt Timer/Service/Testserver/Regeln wieder ab (siehe unten) | | `check_openvpn.py` | Eigenstaendiges Client-Tool: prueft ob ein OpenVPN-Server erreichbar ist (siehe unten) | | `LAENDERCODES.md` | Referenzliste ISO-Laendercode -> ausgeschriebener Name fuer `countries` in `geoblock.ini` | ## Installation Auf dem zu schuetzenden Host (root, `iptables`+`ipset`+`python3` installiert): ```bash git clone https://git.hacker-net.de/Aria-Software/geoblocking-python-script.git cd geoblocking-python-script sudo ./install_geoblock.sh ``` Das: - installiert `geoblock.py` + `geoblock.ini.example` nach `/opt/geoblock/` und erzeugt daraus, falls noch keine vorhanden ist, `geoblock.ini` (bestehende `geoblock.ini` wird NIE ueberschrieben) - legt `/var/log/geoblock/` an - installiert + aktiviert `geoblock.timer` (`systemctl enable --now`) **Wichtig:** `geoblock.ini` ist eure lokale, echte Konfiguration (Laender, Ports, ggf. Proxy-Zugangsdaten) — die Datei steht deshalb in `.gitignore` und wird NIE ins Repo committed. Vorlage/Referenz ist `geoblock.ini.example` (die landet im Repo und wird bei jeder Installation frisch mitkopiert). Wer das Script direkt aus dem Repo-Verzeichnis heraus nutzt (ohne `install_geoblock.sh`), muss die `.ini` einmal selbst anlegen: ```bash cp geoblock.ini.example geoblock.ini ``` **Der Test-Webserver wird standardmaessig NICHT installiert.** Wer ihn braucht (siehe Abschnitt "Verbindungstest" unten), haengt die Option `--with-testserver` an: ```bash sudo ./install_geoblock.sh --with-testserver # optional mit eigenem Zielverzeichnis: sudo ./install_geoblock.sh --with-testserver /opt/geoblock ``` Nur dann wird `geoblock_testserver.py` kopiert und `geoblock-testserver.service` installiert + gestartet (offener Port 8899 standardmaessig). Ohne die Option bleibt der Port zu — nachtraeglich installieren geht jederzeit per erneutem Aufruf mit `--with-testserver`. **Der Reassert-Watchdog wird standardmaessig ebenfalls NICHT installiert.** Er wird nur auf Hosts mit einer gemanagten Firewall gebraucht (Endian, Shorewall, firewalld, UFW, UTM-Appliances), die die iptables-Chain periodisch neu baut und dabei fremde Regeln nach unten schiebt (siehe Abschnitt "Regeln bleiben nicht oben" unten). Aktiviert per `--with-watchdog`: ```bash sudo ./install_geoblock.sh --with-watchdog # kombinierbar mit --with-testserver und Zielverzeichnis: sudo ./install_geoblock.sh --with-testserver --with-watchdog /opt/geoblock ``` Nur dann werden `geoblock-reassert.service` + `geoblock-reassert.timer` installiert und aktiviert (prueft alle 2 Min die Regel-Position). Auch das laesst sich jederzeit per erneutem Aufruf mit `--with-watchdog` nachziehen. > **Zusammengefasst:** `install_geoblock.sh` **ohne** Optionen installiert > **weder** Testserver **noch** Watchdog — nur das eigentliche Geoblocking > (`geoblock.py` + `geoblock.timer`, Apply beim Boot + taeglich). Testserver und > Watchdog sind jeweils opt-in ueber ihre Option. Danach `/opt/geoblock/geoblock.ini` anpassen (Laenderliste!) und einmal manuell testen: ```bash sudo systemctl start geoblock.service journalctl -u geoblock.service -f ``` Der Timer laeuft danach automatisch: **~2 Minuten nach jedem Systemstart** und **einmal taeglich** (mit bis zu 15min Zufallsversatz, damit nicht alle Hosts gleichzeitig ipdeny.com anfragen). Verpasste Laeufe (Host war aus) werden dank `Persistent=true` beim naechsten Boot nachgeholt. ## Deinstallation `uninstall_geoblock.sh` macht die Installation wieder rueckgaengig. Zwei Modi: ```bash # Alles runter: Timer/Service, Testserver (falls installiert), # iptables/ipset-Regeln, Installationsverzeichnis sudo ./uninstall_geoblock.sh # Nur den Test-Webserver entfernen, Geoblocking selbst bleibt aktiv sudo ./uninstall_geoblock.sh --testserver-only ``` Weitere Optionen: ```bash sudo ./uninstall_geoblock.sh --purge-logs # zusaetzlich /var/log/geoblock loeschen sudo ./uninstall_geoblock.sh /opt/geoblock # abweichendes Zielverzeichnis (Default: /opt/geoblock) ``` Ohne `--purge-logs` bleiben die bisherigen Logs unter `/var/log/geoblock` erhalten. Bei der Komplett-Deinstallation wird VOR dem Loeschen automatisch `geoblock.py --remove` ausgefuehrt, damit die iptables-Regel und das `ipset` sauber aus dem Kernel entfernt werden (kein verwaistes DROP, keine offene Verbindung bleibt blockiert). Fehlen Script/Config bereits, wird gewarnt und man sollte manuell pruefen: ```bash iptables -L -n | grep -i geoblock ipset list -n | grep -i geoblock ``` ## Manuelle Nutzung (ohne systemd) ```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. **Achtung:** iptables-Regeln sind nicht reboot-persistent. Wer das 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 ``` - `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 Bereichen ... ` 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 . > …`) 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** (Endian, Shorewall, firewalld, UFW, UTM-Appliances 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 Abhaengigkeiten (nur Python-Stdlib). Er beantwortet jeden Request mit einer kleinen Info-Seite (Client-IP, Zeitstempel, User-Agent) und loggt jeden Request. Er weiss selbst NICHTS von Laendern/Blocklisten — der eigentliche Test ist der Netzwerkeffekt: **geblockte** IPs erreichen ihn gar nicht erst (iptables droppt vor dem TCP-Handshake), **erlaubte** IPs bekommen die Seite. Wird nur installiert, wenn `install_geoblock.sh` mit der Option `--with-testserver` aufgerufen wurde (Default: nicht installiert, siehe Abschnitt "Installation" oben). Ist er installiert, laeuft er als `geoblock-testserver.service` auf Port `8899` (einstellbar unter `[testserver]` in `geoblock.ini`, siehe unten). **Testablauf:** ```bash # 1) Von einem ERLAUBTEN Netz/Land aus aufrufen -> sollte laden curl http://:8899/ # 2) Testweise den eigenen Laendercode (z.B. DE) in geoblock.ini # eintragen und aktivieren: sudo python3 geoblock.py --config geoblock.ini --apply # 3) Jetzt von einem Client MIT einer IP aus diesem Land connecten # (z.B. Handy im Mobilfunknetz) -> sollte jetzt TIMEOUT geben curl --max-time 5 http://:8899/ # 4) Land wieder aus der .ini entfernen + --apply -> Zugriff geht wieder ``` Manuell ohne systemd starten: ```bash python3 geoblock_testserver.py --config geoblock.ini # oder komplett ohne .ini: python3 geoblock_testserver.py --port 8899 --bind 0.0.0.0 \ --log-file /var/log/geoblock/testserver.log ``` Requests landen (Zeit, Client-IP, Methode/Pfad, User-Agent) in der unter `log_file` im `[testserver]`-Abschnitt konfigurierten Datei. **Achtung Firewall/Portfreigabe:** der Testserver selbst oeffnet nur den Port lokal — ob er von aussen erreichbar ist, haengt von eurer sonstigen Firewall/Portweiterleitung ab (das ist bewusst getrennt von den Geoblock-DROP-Regeln, die ja genau diesen Port betreffen sollen). ## Gezieltes Blocken einzelner Ports (Selbstaussperr-Schutz) Per Default blockt `--apply` **alle** Ports/Protokolle fuer die gelisteten Laender — das ist klassisches Geoblocking auf Host-Ebene. Das ist beim Testen riskant: testet man testweise den eigenen Laendercode (siehe oben), sperrt man sich damit auch von SSH & Co. aus, falls der Test-Client zufaellig aus demselben Land connectet. Ueber `ports` (und optional `protocol`) in der `.ini` laesst sich das Blocking auf einzelne Anwendungen eingrenzen. Beispiel: nur den Testserver-Port blocken, SSH bleibt fuer alle Laender offen: ```ini [geoblock] countries = de ... ports = 8899 protocol = tcp ``` `--apply` legt dann statt einer allgemeinen DROP-Regel eine mit `-p tcp -m multiport --dports 8899` an — matched nur Pakete zu Port 8899, alles andere (inkl. Port 22) bleibt unberuehrt. Mehrere Ports/Bereiche gehen kommaseparat (`80,443,8000:9000`), mehrere Protokolle ebenso (`protocol = tcp,udp`, legt dann eine Regel pro Protokoll an). `--status` zeigt an, ob gerade eine Port-Einschraenkung aktiv ist und welche. `--remove` entfernt automatisch die zur aktuellen `.ini` passenden Regel-Varianten — falls `ports`/`protocol` zwischen zwei Laeufen geaendert wurden, lohnt sich vor dem naechsten `--apply` ein Blick mit `iptables -L INPUT -n --line-numbers`, ob noch Altregeln mit der vorherigen Variante stehen. ## Logging In `geoblock.ini` unter `log_file` einen Pfad eintragen (Default: `/var/log/geoblock/geoblock.log`, wird vom Installer angelegt). Bei jedem Lauf wird eine Zeile angehaengt, u.a. eine maschinenlesbare `RESULT status=OK/ERROR ...`-Zeile — genau die wertet das checkmk-Plugin aus. Leer lassen = keine Datei-Logs (nur stderr/systemd-Journal). ## checkmk-Monitoring `geoblock_checkmk` ist ein klassisches checkmk **local check**-Plugin (kein extra Agent-Plugin-Verzeichnis noetig). Manuell einrichten: 1. Im Script-Kopf `LOG_FILE` auf den Pfad aus der `.ini` (`log_file`) anpassen. 2. Ausfuehrbar machen und kopieren: ```bash chmod 755 geoblock_checkmk cp geoblock_checkmk /usr/lib/check_mk_agent/local/ ``` (Pfad haengt vom Setup ab — bei OMD-Sites z.B. `~/local/lib/check_mk_agent/local/`.) 3. Naechster Agent-Abruf zeigt den Service **Geoblock**. Status-Logik: `ERROR` im letzten Lauf → CRIT. Letzter erfolgreicher Lauf > 36h alt → WARN, > 72h alt → CRIT (Timer laeuft taeglich, das faengt einen einzelnen verpassten Lauf ab, ohne sofort zu alarmieren). Sonst OK, inkl. Perfdata (Alter in Sekunden, Anzahl geblockter IP-Bereiche). ## Konfiguration (`geoblock.ini`) Siehe Kommentare in der Datei selbst — kurz: - `countries`: Komma-Liste ISO-3166-1-alpha-2 Codes (z.B. `RU, CN, KP, IR`) - `chain`: iptables-Chain (`INPUT` = eingehender Traffic zu diesem Host) - `interface`: optional, nur ein Interface pruefen - `log`: iptables-LOG-Eintrag vor dem DROP (dmesg/kern.log) an/aus - `ipset_name`: Name des ipset-Sets - `log_file`: Pfad zur script-eigenen Log-Datei (fuer checkmk) - `ports`: optional, Komma-Liste von Ports/Bereichen (z.B. `80,443,8000:9000`). Leer = alle Ports/Protokolle werden geblockt (klassisches Geoblocking). Gesetzt = nur diese Ports werden fuer die gelisteten Laender geblockt, der Rest des Hosts bleibt erreichbar. Siehe Abschnitt "Gezieltes Blocken einzelner Ports" unten. - `protocol`: nur relevant wenn `ports` gesetzt ist — `tcp` (Default), `udp` oder `tcp,udp` - `proxy`: optional, HTTP(S)-Proxy fuer den Abruf der Zonefiles von ipdeny.com (z.B. `http://10.0.0.5:3128`, mit Auth `http://user:pass@host:port`). Nur noetig, wenn der Host selbst keine direkte Internetverbindung hat, aber ueber einen Proxy raus kann. Leer lassen = direkte Verbindung (Standard). Siehe Abschnitt "Proxy fuer den Zonefile-Abruf" unten. Abschnitt `[testserver]` (fuer `geoblock_testserver.py`): - `port`: TCP-Port des Test-Webservers (Default `8899`) - `bind`: Bind-Adresse (`0.0.0.0` = alle IPv4-Interfaces) - `log_file`: Pfad zur Request-Log-Datei des Testservers ## Betrieb hinter einem Reverse-Proxy (z.B. nginx) Wichtig zu verstehen, weil es leicht zu falschen Testergebnissen fuehrt: **Geoblocking mit iptables/ipset arbeitet auf Netzwerk-Ebene (TCP), nicht auf HTTP-Ebene.** Es sieht immer nur die IP, die die TCP-Verbindung tatsaechlich zum jeweiligen Host aufbaut — unabhaengig davon, was in HTTP-Headern steht. **Wo die echte Client-IP ankommt, haengt vom Aufbau ab:** - **nginx ist der einzige "Proxy" (direkt am Internet, kein CDN/WAF davor):** Die TCP-Verbindung vom Client landet direkt am nginx-Host. Dort sieht sowohl iptables als auch nginx selbst die echte oeffentliche Client-IP. In diesem Setup **muessen die Geoblock-Regeln auf dem nginx-Host greifen**, und zwar auf dem Port, auf dem nginx nach aussen lauscht (z.B. 80/443) — NICHT auf dem internen Port des dahinterliegenden Testservers/Backends. - **Davor haengt zusaetzlich ein CDN/WAF (Cloudflare, Fastly, ...):** Dann kommt am nginx-Host nur noch die IP des CDN-Rechenzentrums an, nicht die echte Client-IP. iptables auf dem nginx-Host wuerde dann effektiv das CDN blocken, nicht den Endnutzer — Geoblocking muesste in diesem Fall beim CDN selbst konfiguriert werden (die meisten bieten das nativ an). - **Der Testserver haengt NUR intern hinter nginx** (z.B. `proxy_pass` auf `127.0.0.1:8899`): Der Testserver selbst sieht dann nur die IP von nginx (`127.0.0.1`), nicht die des echten Clients — die Verbindung des Clients wurde ja schon vorher bei nginx "verbraucht" und neu aufgebaut. Ein iptables-DROP auf Port 8899 wuerde in diesem Aufbau **gar nichts bewirken**, weil der Traffic zum Testserver nie ueber das oeffentliche Netz laeuft, sondern nur lokal von nginx zum Backend. **Praktische Konsequenz fuer den Verbindungstest:** Um den Testserver (`geoblock_testserver.py`, siehe oben) sinnvoll von aussen zu testen, muss er entweder: 1. selbst direkt mit einer oeffentlichen IP/Port erreichbar sein (kein Proxy dazwischen) — z.B. indem die Testmaschine testweise eine eigene oeffentliche IP bekommt, oder 2. hinter nginx haengen, wobei dann **nginx** (nicht der Testserver) der Punkt ist, an dem die Geoblock-Regeln greifen muessen. Fuer einen sauberen End-to-End-Test der iptables-Regeln selbst (ohne Fragen zu Proxy-Ketten/CDN reinzumischen) ist Variante 1 — Testmaschine direkt mit eigener oeffentlicher IP, Geoblocking direkt auf dieser Maschine — der einfachste und eindeutigste Weg. ## Betrieb hinter einem AWS Load Balancer (ALB vs. NLB) Gleiches Grundproblem wie beim Reverse-Proxy-Abschnitt oben, nur mit AWS-spezifischer Antwort — **ob die echte Client-IP am Server ankommt, haengt vom Load-Balancer-Typ ab:** - **Application Load Balancer (ALB):** Arbeitet auf **Layer 7 (HTTP)**. Der ALB terminiert die TCP-Verbindung vom Client komplett und baut eine **neue** TCP-Verbindung zum Backend auf — mit der **IP des ALB-Nodes** (private VPC-IP) als Absender, nicht der des Clients. Die echte Client-IP steckt nur noch im HTTP-Header `X-Forwarded-For`. iptables/ipset sehen aber ausschliesslich die TCP-Ebene, also die ALB-Node-IP — **Geoblocking auf dem Backend bringt hinter einem ALB nichts**, es wuerde effektiv die eigene AWS-Infrastruktur "blocken" bzw. gar nichts filtern. Optionen in diesem Fall: AWS WAF mit Geo-Match-Regel direkt am ALB (die vorgesehene AWS-Loesung dafuer), oder auf NLB wechseln, falls auf L7-Features wie Pfad-Routing verzichtet werden kann. - **Network Load Balancer (NLB):** Arbeitet auf **Layer 4**, terminiert die TCP-Verbindung NICHT, sondern leitet sie durch. Die Quell-IP im TCP/IP-Paket, das am Server ankommt, ist die **echte Client-IP** — genau das, was iptables/ipset zur Filterung brauchen. `geoblock.py` funktioniert hinter einem NLB in der Standard-Konfiguration also **wie auf einem Server ohne Load Balancer davor**. - **Standardfall (funktioniert direkt):** Targets liegen im selben VPC und sind als **Instance-Target** registriert — die echte Client-IP kommt transparent im TCP-Paket an, nichts weiter zu konfigurieren. - **Vorbehalt:** Bei **IP-Targets** oder wenn der NLB ueber **PrivateLink**/Cross-VPC/Cross-Account angesprochen wird, bewahrt AWS die Client-IP nur, wenn am NLB **Proxy Protocol v2** aktiviert ist. Ist das der Fall, kommt die IP nicht mehr "nackt" im TCP-Paket an, sondern als PROXY-Header vor dem eigentlichen Payload — iptables sieht dann weiterhin nur die NLB-Node-IP, die echte IP muesste applikationsseitig aus dem PROXY-Header ausgewertet werden, nicht von iptables. In diesem Fall vorher pruefen, ob Proxy Protocol aktiv ist. **Kurz zusammengefasst:** ALB → Geoblocking auf dem Backend funktioniert nicht (WAF am ALB nutzen). NLB mit Instance-Targets im selben VPC (Standard) → `geoblock.py` funktioniert unveraendert. NLB mit IP-Targets/PrivateLink → vorher pruefen, ob Proxy Protocol v2 aktiv ist. ## Proxy fuer den Zonefile-Abruf (`proxy` in `geoblock.ini`) **Wichtig — das ist ein ANDERES Thema als der Reverse-Proxy-Abschnitt oben.** Dort ging es darum, wo die *eingehende* Client-Verbindung ankommt. Hier geht es darum, wie `geoblock.py` selbst *ausgehend* die Zonefiles von ipdeny.com laedt. **Wann noetig:** Manche Hosts sind zwar aus dem Internet erreichbar (z.B. ueber einen vorgelagerten Reverse-Proxy oder eine Firewall mit Portweiterleitung), haben aber selbst **keine direkte ausgehende Internetverbindung** — nur einen Weg ueber einen internen HTTP(S)-Proxy nach draussen. Ohne diesen Proxy kann `geoblock.py` `https://www.ipdeny.com/...` nicht erreichen und bricht mit einem URL-Fehler ab. **Konfiguration** in `geoblock.ini`, Abschnitt `[geoblock]`: ```ini [geoblock] countries = de proxy = http://10.0.0.5:3128 ; mit Basic-Auth am Proxy: ; proxy = http://user:pass@10.0.0.5:3128 ``` Der Proxy wird sowohl fuer HTTP- als auch HTTPS-Abrufe genutzt (ipdeny liefert per HTTPS aus). Leer lassen (Standard) = das Script verbindet sich direkt, ohne Proxy — das ist der Normalfall auf den meisten Servern. Betroffen ist **nur** der Abruf der Zonefiles beim Ausfuehren von `--apply`/`--update`. iptables/ipset selbst und der Testserver brauchen dafuer keinen Proxy — die filtern nur lokal am Kernel-Paketfilter. ## Verbindungstest fuer OpenVPN-Server (`check_openvpn.py`) Eigenstaendiges Client-Tool (nur Python-Stdlib, keine Abhaengigkeiten), um zu pruefen ob ein OpenVPN-Server unter einer Host/Port/Protokoll-Kombination erreichbar ist — ohne dafuer eine echte VPN-Verbindung mit Zertifikaten aufzubauen. Das Gegenstueck zu `geoblock_testserver.py`, wenn ihr das Geoblocking (oder eine sonstige Firewall-Regel) gegen einen echten OpenVPN-Server statt gegen den Test-Webserver pruefen wollt — z.B. genau fuer den Fall "Geoblocking auf dem VPN-Server hinter dem NLB" von eben. **Wie es funktioniert:** - **UDP** (Standard, meist Port `1194/udp`): schickt ein echtes, protokoll- konformes OpenVPN-Handshake-Paket (`P_CONTROL_HARD_RESET_CLIENT_V2`, der erste Schritt den auch ein echter Client macht). Antwortet der Server mit seinem eigenen Hard-Reset-Paket, ist er zweifelsfrei erreichbar. - **TCP** (falls der Server mit `proto tcp-server` laeuft): einfacher TCP-Connect-Test. **Benutzung:** ```bash python3 check_openvpn.py --host vpn.example.com # -> Standard: UDP, Port 1194, 3 Versuche, 3s Timeout je Versuch python3 check_openvpn.py --host vpn.example.com --port 443 --proto tcp python3 check_openvpn.py --host vpn.example.com --tries 5 --timeout 2 ``` Exit-Codes fuer Skripte/Monitoring: `0` = erreichbar, `1` = nicht erreichbar/ Timeout, `2` = Aufruf-Fehler (z.B. Host nicht aufloesbar). **Wichtiger Hinweis zu `tls-auth`/`tls-crypt`:** Ist das auf dem Server konfiguriert (haeufige, empfohlene Haertung), verwirft er JEDES Paket ohne gueltiges HMAC/Verschluesselung bereits VOR jeder Antwort — komplett stillschweigend, identisch zum Verhalten bei einer Firewall-Blockade. Ein Timeout bei diesem Tool bedeutet also nicht zwingend "nicht erreichbar", sondern kann auch "erreichbar, aber tls-auth/tls-crypt aktiv" heissen. Wer das eindeutig unterscheiden will: testweise tls-auth/tls-crypt kurz deaktivieren und erneut testen, oder direkt mit einem echten OpenVPN-Client gegentesten.