Die Idee kam mir beim Schreiben des vorherigen Beitrages (dovecot, XBL, hosts.deny).
Da inzwischen auf dem Server auch SSH Passwörter/User im Stundentakt probiert werden, für dessen Distribution aber leider kein funktionierendes Paket für libpam_geoip existiert, möchte ich die Variante hosts.deny mit GeoIP anhand von SSH beschreiben. Die Verwendung für andere Dienste (pop, imap...) kann leicht abgeleitet werden. Verwendet wird die GeoLite Legacy Database.
Sshd unterstützt hosts.deny üblicherweise ohne extra Wrapper.
Ob die Unterstützung funktioniert, kann relativ einfach geprüft werden.
strings -f /usr/sbin/sshd | grep hosts_access
Der erste Schritt ist wieder das Holen einer frischen GeoIP Datenbank bzw. ein Skript zum aktuell Halten.
update-geoip-db.sh
#!/bin/bash
# Fetch fresh geoip country db from maxmind
# Use with cron once a month
BASE='/usr/local/share/GeoIP'
FILENAME='GeoIP.dat'
URL='http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz'
cd $BASE
wget -q $URL
STATUS=$?
if [ $STATUS -eq 0 -a -f $FILENAME.gz ]
then
if [ -f $FILENAME ]
then
mv $FILENAME ${FILENAME}_old
fi
gunzip $FILENAME.gz
else
echo "Problem when trying to fetch $URL" | mail -s 'update-geoip-db' user@mail.tld
exit 1
fi
exit 0
Der Mailempfänger im Skript (falls beim Download was schief läuft) muss natürlich angepasst werden.
Dieses Skript einmal monatlich via CRON ausführen.
Als nächstes brauchen wir ein Skript, das 1 / 0 ausgibt, je nachdem, ob das Land erlaubt ist oder nicht.
checkcountry.sh
#!/bin/bash
# check ip address in local geodb
# use with /etc/hosts.deny
# author: xela
GEODB='/usr/local/share/GeoIP/GeoIP.dat'
ALLOW=('AT' 'DE' 'CH' 'FL')
if [ -z $1 ]
then
exit 1
fi
if [ ! -f $GEODB ]
then
exit 1
fi
if ! [[ $1 =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]
then
exit 1
fi
RES=$(geoiplookup -f $GEODB $1)
CNTRY=$(echo $RES | awk -F ',' '{print $1}' | awk -F ': ' '{print $2}')
if [ "$CNTRY" == 'IP Address not found' ]
then
exit 1
fi
for i in "${ALLOW[@]}"
do
if [ "$i" == "$CNTRY" ]
then
logger -t 'CheckCountry' "Permitting $1 ($RES) to authenticate."
exit 1
fi
done
# allowed and unknown got sortet out, block the rest
exit 0
Und schließlich brauchen wir noch einen Eintrag in der /etc/hosts.deny
# limit access to certain countries
sshd: ALL : aclexec /usr/local/bin/checkcountry.sh %a
Ab jetzt gilt die Limitierung, es muss kein Dienst neu gestartet werden.
Eine gute Idee ist es noch, in die hosts.allow ein paar ausgewählte IPs zu stellen, von denen man üblicherweise einloggt.
Es wirkt:
grep 'refused connect' /var/log/auth.log | less
Selbstverständlich gibt es noch viele weitere Methoden, wie SSH abgesichert werden kann und soll.
Auf die wird hier aber nicht eingegangen.
UPDATE: da die GeoLite Legacy nicht mehr weiter gepflegt wird, ist ein Wechsel zur neueren Variante GeoLite2 (mmdb) nötig. Lest dazu den aktuellen Artikel Zugang zu Diensten mit tcpwrap und GeoLite2 einschränken