It’s been a while that I started to use GeoIP and in the last years it seems that the methods of installing GeoIP changed, including the registration. The service is still free and my recommendation is to install it from the according sources of your Linux distribution.
For my Debian 11 this was easily done:
apt install geoip-bin geoip-database geoipupdate mmdb-bin
To check whether it’s working we five it a try with a Google server:
/usr/bin/mmdblookup -f /var/lib/GeoIP/GeoLite2-City.mmdb -i 8.8.4.4
If everything goes well we will get a JSON returned containing many information like country name and country code, latitude, longitude and sometimes much more (including values in different languages).
Once this is working we create a little script which will get an IP address as an input and depending on the GeoIP data will return either an “ALLOW” or “DENY”. As you might have guessed those look exactly like values the /etc/hosts.allow file is happy to deal with. Here comes my version of this script for the SSH service:
#!/usr/bin/env bash
# UPPERCASE space-separated country codes to ACCEPT
ALLOW_COUNTRIES="DE" # Make sure you add your Country code here
# Space-separated list of IPs to whitelist
# You can use CIDR notation as well, IE: 192.168.1.0/24
ALLOW_IPS="123.123.123.123" # You can add your public IP address here
if [[ $# -ne 1 ]]; then
echo "Usage: `basename $0` <ip>" 1>&2
exit 0 # return true in case of config issue
fi
# Fixed static IP
echo $1 | /usr/bin/grepcidr "$ALLOW_IPS" &> /dev/null
if [ ${PIPESTATUS[1]} -eq 0 ]; then
RESPONSE="ALLOW"
logger "$RESPONSE sshd connection from $1 (Whitelisted)"
exit 0
fi
COUNTRY="$(/usr/bin/mmdblookup -f /var/lib/GeoIP/GeoLite2-City.mmdb -i "$1" country iso_code 2>&1| awk -F '"' '{ print $2 }'|head -n 2|tail -n 1)"
[[ $COUNTRY = "IP Address not found" || $ALLOW_COUNTRIES =~ $COUNTRY ]] && RESPONSE="ALLOW" || RESPONSE="DENY"
if [ $RESPONSE = "ALLOW" ]
then
logger -p authpriv.notice "$RESPONSE sshd connection from $1 ($COUNTRY)"
exit 0
else
logger -p authpriv.notice "$RESPONSE sshd connection from $1 ($COUNTRY)"
exit 1
fi
Put the script somewhere like /usr/bin/geoipservice.sh
and make it executable using chmod 755 /usr/bin/geoipservice.sh
. Now you can try it using
/usr/bin/geoipservice.sh 8.8.8.8
Don’t be surprised if you don’t see any output – that’s basically a good sign 🙂 To see the result please check your log files and you will find something like
Sep 07 17:10:09 www.example.org user [1691149]: DENY sshd connection from 8.8.8.8 (US)
This is correct as I only allowed machines from Germany (DE) to connect in the script. The script is not specific for any service, you can use it for several services, just adjust the logging message so you won’t confuse yourself (this guy talks from experience here!).
The last step is now to edit the /etc/hosts.allow
file and add the following line to it:
IMPORTANT:
Before you do that ensure that you whitelisted whatever IP you usually use to access the service, especially losing access of SSH can become pretty cumbersome pretty quickly here.
sshd: ALL: aclexec /usr/bin/geoipsshd.sh %a
If you want to take it further from here fail2ban or the newer crowdsec would be the next stops to lock your pretty machine further down.