pf GeoIP Blocking
Warum eigentlich?
Manchmal sieht man in seinen Serverlogs komische Aufrufe, egal ob das nun access logs von einem Apache oder nginx sind, oder die Meldungen von z.B. sshd. Vielleicht wird man aber auch gerade aktiv von irgendjemanden attackiert und möchte diese Person aussperren, dabei kommen jedoch immer unterschiedliche IP Adresse. Wenn man diese dann nachschlägt stellt man fest, dass sie immer aus dem selben Land, oder sogar dem selben ISP kommen. Das kann man mit Tools wie zum Beispiel whois auf der Kommandozeile heraus finden.
Eventuell möchte man aber auch seinen Inhalt nur in bestimmten Ländern bereitstellen, auch das kann man mit Geo Blocking erreichen.
Es gibt diverse Gründe warum man bestimmte Personen von seinem Server aussperren möchte. Man sollte aber immer daran denken, dass man sich selbst nicht versehentlich mit aussperrt. Bevor du also in eines der Länder fährst die du auf deinem Server blockierst, solltest du dich lieber freischalten (oder einen VPN verwenden).
Installation
Um überhaupt IP Adressen einem Land zuordnen zu können, benötigen wir eine Datenbank, die diese notwendigen Daten bereitstellen kann. In den FreeBSD Ports befindet sich die das Paket ipdbtools. Mit diesem Paket bekommt man ein paar Werkzeuge, um lokal eine IP Datenbank zu speichern und diese aktuell zu halten. Die Daten stammen von den RIR, sind also identisch wie die Ergebnisse die einem beispielsweise ein whois ausgeben würde.
Um das Paket zu installieren, führe folgendes Kommando aus:
sudo pkg install ipdbtools
Ich werde hier nicht weiter auf die verwendung der einzelnen Tools die dieses Paket bereitstellt eingehen, da die manpages dazu genug Informationen liefern. Wer also mehr wissen möchte findet die Infos auf seiner Shell mit:
apropos ipdb
Ich aktualisiere meine lokale Datenbank regelmäßig per Cronjob. Dafür habe ich mir ein kleines Skript geschrieben, welches die nötigen Kommandos ausführt. Alle Daten speichere ich im Verzeichnis /etc/pf-tables/ , das Skript heißt bei mir geoipblocker.sh:
#!/bin/sh
#Aktualisiere die DB
echo "Last Update:" >/etc/pf-tables/geoipupdate.log
date >> /etc/pf-tables/geoipupdate.log
echo "\n">>/etc/pf-tables/geoipupdate.log
ipdb-update.sh >> /etc/pf-tables/geoipupdate.log 2>&1
#Schreibe IP Adressen in Datei, hinter -t stehen die jeweiligen Länder.
# Für neue Länder einfach eine Zeile ergänzen
ipup -t RU -p > /etc/pf-tables/geoipblocker.table
ipup -t CN -p >> /etc/pf-tables/geoipblocker.table
ipup -t MN -p >> /etc/pf-tables/geoipblocker.table
#Lade die Datei in einen pf table
pfctl -t geoipblocker -T replace -f /etc/pf-tables/geoipblocker.table >> /etc/pf-tables/geoipupdate.log 2>&1
Nun müssen wir noch unsere Firewall Regeln anpassen. Jeder hat hier unterschiedlich viele Dinge bereits drin stehen. Falls die Datei noch leer ist, empfehle ich erstmal eine Basis Konfiguration vorzunehmen und dann zu ergänzen.
In die /etc/pf.conf muss dann folgendes ergänzt werden:
# Erstelle einen Table
table <geoipblocker> persist file "/etc/pf-tables/geoipblocker.table"
# blockiere alle IPs in diesem table
block quick on $external_if from <geoipblocker> to any
Bitte daran denken $external_if gegen dein passendes Interface, beziehungsweise der passenden Variable zu ersetzen. Das “quick” in der Regel sorgt dafür, dass Pakete die mit dieser Regel matchen nicht weiter ausgewertet werden. Falls du in deinem Regelsatz bereits Port Forwarding eingestellt hast, solltest du prüfen, dass dort nicht bereits ein “pass” drin steht. Da NAT und Redirects vor den Filtern durchgeführt werden, würde dies dafür sorgen, dass deine Internen Dienste trotzdem noch von den gefilterten Adressen erreicht werden können.
Beispielsweise wäre folgende Regel problematisch:
rdr pass on $external_if proto tcp to $server_ext_ip port {http,https} -> $internal_server_ip
Stattdessen sollte man das pass entfernen und die Freigabe in einer separaten Regel durchführen:
pass in on $external_if proto tcp from any to $server_ext_ip port {http,https}
pass in on $external_if proto tcp from any to $internal_server_ip port {http,https}
Nachdem die Regeln zur /ertc/pf.conf hinzugefügt wurden, muss das vorhin erstellte Skript noch ausführbar gemacht und einmal per Hand ausgeführt worden sein. Dann kann man die Firewall neu laden:
chmox +x /etc/pf-tables/geoipblocker.sh
/etc/pf-tables/geoipblocker.sh
pfctl -vf /etc/pf.conf
Beim Ausführen des Skriptes wird die IP Datenbank aktualisiert und die zu den gewählten Ländern gehörenden Adressen in eine Datei geschrieben. Das Skript lädt diese Adressen in den Table geoipblocker. Damit beim Neustarten der Firewall diese Adressen ebenfalls geladen werden, ist der table persistent mit der Datendatei im Regelsatz definiert.
Um zu prüfen, ob die Tabelle korrekt geladen wurde, kann man folgendes auf der Kommandozeile nutzen:
pfctl -t geoipblocker -T show
Das sollte eine lange Liste an Prefixen ausgeben. Wenn man filtern möchte, sollte man nicht grep nutzen, da man damit keine Subnets abfragen kann. Stattdessen nutzt man folgenden Befehl, um zu testen ob eine Adresse im table enthalten ist:
pfctl -t geoipblocker -Tt 1.2.3.4
Nun bleibt nur noch übrig, regelmäßig die IP Daten zu aktualisieren, wofür wir einen Crontab anlegen mit crontab -e. Das öffnet einen Editor, in dem man dann folgenden Inhalt schreiben kann:
0 3 * * 1 /etc/pf-tables/geoipblocker.sh
Das sorgt dafür, dass die Daten jeden Montag um 3 Uhr morgens aktualisiert werden.
Fazit
Mit den ipdbtools kann man in Verbindung mit pf sein Netzwerk für bestimmte Länder sperren. Man sollte natürlich vorsichtig sein, dass man sich nie selbst aussperrt, weil man zum Beispiel im Urlaub ist. Selbstverständlich funktionieren die hier hinzugefügten Regeln auch für IPv6! Da Produkte wie zum Beispiel pfSense die pf Firewall nutzen, funktioniert das dieses Setup dort auch.