Limit your SSH logins using GeoIP

Sources:


Install

First install GeoIP binary for Linux and their database based on your operating system. For CentOS and RedHat users GeoIP binary and database are combined in single package:
# yum install GeoIP

Make sure it works by doing a simple test:
# geoiplookup 8.8.8.8
GeoIP Country Edition: US, United States

Create the SSH/FTP Filter Script

This simple shell script which returns either an true (ACCEPT) or false (DENY) response, and if it's a DENY it logs to the system messages (using logger). Save this file in /usr/local/bin/ipfilter.sh:
# vim /usr/local/bin/ipfilter.sh

#!/bin/bash
# UPPERCASE space-separated country codes to ACCEPT
ALLOW_COUNTRIES="NZ AU"

if [ $# -ne 1 ]; then
  echo "Usage:  `basename $0` <ip>" 1>&2
  exit 0 # return true in case of config issue
fi

COUNTRY=`/usr/bin/geoiplookup $1 | awk -F ": " '{ print $2 }' | awk -F "," '{ print $1 }' | head -n 1`

[[ $COUNTRY = "IP Address not found" || $ALLOW_COUNTRIES =~ $COUNTRY ]] && RESPONSE="ALLOW" || RESPONSE="DENY"

if [ $RESPONSE = "ALLOW" ]
then
  exit 0
else
  logger "$RESPONSE sshd connection from $1 ($COUNTRY)"
  exit 1
fi

Edit the ALLOW_COUNTRIES to include a list of country codes you want your SSH server to accept connections from.

Note: is an IP address cannot be matched to a country (such as an internal IP address), the connection is accepted too (see the $COUNTRY = "IP Address not found").

Don't forget to make the script executable:
# chmod +x /usr/local/bin/ipfilter.sh

Lock down SSH & HTTP

The last things we need to do is tell the ssh daemon (sshd) to deny all connections (by default) and to run this script to determine whether the connection should be allowed or not.
In '/etc/hosts.deny' add the line:
echo "sshd: ALL" >> /etc/hosts.deny
echo "httpd: ALL" >> /etc/hosts.deny

and in '/etc/hosts.allow' add the line:
echo "sshd: ALL: spawn /usr/local/bin/ipfilter.sh %a" >> /etc/hosts.allow
echo "httpd: ALL: spawn /usr/local/bin/ipfilter.sh %a" >> /etc/hosts.allow

Updating GeoIP

In order to make sure you are up-to-date with your GeoIP (free) country database, you will need to write another script which you can run as a monthly root cron job.
# vim /root/scripts/geoip.sh

#!/bin/bash

cd /root/tmp
wget -q https://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
wget -q https://geolite.maxmind.com/download/geoip/database/GeoIPv6.dat.gz
if [ -f GeoIP.dat.gz ]
then
gzip -d GeoIP.dat.gz
gzip -d GeoIPv6.dat.gz
rm -f /usr/share/GeoIP/GeoIP.dat
rm -f /usr/share/GeoIP/GeoIPv6.dat
mv -f GeoIP.dat /usr/share/GeoIP/GeoIP.dat
mv -f GeoIPv6.dat /usr/share/GeoIP/GeoIPv6.dat
else
echo "The GeoIP library could not be downloaded and updated"
fi

Don't forget to make the script executable:
# chmod +x /root/scripts/geoip.sh

Schedule the job:
# vi /etc/crontab

  *  *  1  *  * /root/scripts/geoip.sh