Manche Dienste möchte man nur in bestimmten Ländern zulassen. So braucht wohl kaum jemand SSH-Zugriff von China aus auf seinen Heimserver in Deutschland. Hier macht ein Geo-IP-Blocker Sinn.
Beim Stöbern im Internet bin ich auf unterschiedliche Lösungen gestoßen und immer war eine essentielle Frage, wie man dennoch den Zugriff vom internen Netzwerk aus zulassen kann. Meine relativ simpel gehaltene Lösung beinhaltet auch ein eigenes Bash-Skript, welches IP-Adressen aus angegebenen IP-Adressbereichen, die als lokal angesehen werden, konsolidiert.
Zunächst brauchen wir die nötigen Tools, die jede gängige und aktuelle Linux-Distribution als Pakete mit anbietet:
- mmdblookup (Debian-Paket mmdb-bin)
- grepcidr
Anschließend sollte man die aktuelle Datenbank GeoLite2 Country herunterladen, entpacken und platzieren:
root@server:~# wget https://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz
root@server:~# tar -xvzf GeoLite2-Country.tar.gz
root@server:~# mkdir /usr/share/GeoIP
root@server:~# mv GeoLite2-Country_*/GeoLite2-Country.mmdb /usr/share/GeoIP/
Wenn das geschehen ist, kann man schon einmal testen, ob eine einfache Abfrage funktioniert. Es sollte etwa so aussehen:
root@server:~# mmdblookup -f /usr/share/GeoIP/GeoLite2-Country.mmdb -i 8.8.8.8 country iso_code
"US" <utf8_string>
Da hier vor und hinter der Ausgabe je eine Leerzeile existiert (ist in diesem Code-Baustein leider nicht möglich einzubauen) und die Ausgabe noch gefiltert werden kann, erweitern wir die Abfrage wie folgt:
root@server:~# mmdblookup -f /usr/share/GeoIP/GeoLite2-Country.mmdb -i 8.8.8.8 country iso_code 2>/dev/null | grep -vE '^$' | cut -d'"' -f2
US
Das ist das Ergebnis, welches wir haben wollen. Wird eine IP nicht gefunden, ist die Ausgabe einfach leer. Damit lässt sich bereits arbeiten. Fehlt noch ein lokaler Adressbereich. Hier kommt in meinem Skript das Tool grepcidr zum Einsatz:
#!/bin/bash
if [ $# -lt 3 ]
then
echo "You must specify three arguments:"
echo "- The IP you want to look up"
echo "- Allowed country codes (or LAN for local network ranges)"
echo "- The network ranges for local"
exit 2
elif ! [ -f /usr/bin/mmdblookup ]
then
echo "You must install mmdblookup tool in order to use this script."
exit 3
elif ! [ -f /usr/bin/grepcidr ]
then
echo "You must install grepcidr tool in order to use this script."
exit 3
else
cn=$(/usr/bin/mmdblookup -f /usr/share/GeoIP/GeoLite2-Country.mmdb -i $1 country iso_code 2>/dev/null | /usr/bin/cut -d'"' -f2 | /bin/grep -vE '^$')
if [ "$cn" = "" ]
then
/usr/bin/grepcidr $3 <(echo $1) >/dev/null && echo "LAN" && cn="LAN" || echo "UNKNOWN"
else
echo $cn
fi
if [[ "$2" =~ $cn ]]
then
exit 0
fi
fi
exit 1
Das Skript kann man z. B. unter /usr/bin/gil (Abkürzung für Geo-IP-Lookup) ablegen. Wichtig dabei ist, dass es auch ausführbar ist:
root@server:~# chmod +x /usr/bin/gil
Aufruf / Syntax:
gil <IP> <Accepted Country Codes> <Internal Network Masks>
Beispiele:
root@server:~# gil 8.8.8.8 "DE LAN" "192.168.0.0/24"
US
root@server:~# echo $?
1
root@server:~# gil 134.2.3.4 "DE LAN" "192.168.0.0/24"
DE
root@server:~# echo $?
0
root@server:~# gil 192.168.0.10 "DE LAN" "192.168.0.0/24"
LAN
root@server:~# echo $?
0
Der Rückgabewert von 0 und 1 ist wichtig für den nächsten Schritt. Die Textausgabe ist nur für manuelle Checks gedacht. Kann jeder für sich selbst nach Belieben abwandeln.
SSH-Dienst nur aus Deutschland und LAN zulassen
Zunächst fügen wir die folgende Zeile in die Datei /etc/hosts.allow hinzu:
sshd: ALL: aclexec /usr/bin/gil %a "DE LAN" 192.168.0.0/24
Und dann die folgende Zeile in die Datei /etc/hosts.deny:
sshd: ALL
Es muss kein Dienst neugestartet werden. Die Änderungen werden umgehend gültig. Deshalb ist auch die Reihenfolge wichtig einzuhalten. Sonst sperrt man sich möglicherweise aus.
Wenn es funktioniert hat und Anfragen von nicht akzeptierten IP-Adressen kommen, wird man in der Log-Datei /var/log/auth.log so etwas vorfinden:
May 28 16:31:45 ssh sshd[30376]: aclexec returned 1
May 28 16:31:45 ssh sshd[30376]: refused connect from 58.242.83.31 (58.242.83.31)
May 28 16:31:57 ssh sshd[30383]: aclexec returned 1
May 28 16:31:57 ssh sshd[30383]: refused connect from 37.187.5.137 (37.187.5.137)