Frustrate SSH Scanners with Iptables GeoIP Blocking

Script Kiddie
Your adversary.

Updated 2018:  Still working! xt_geoip_dl is now available and handles Geolite2 database downloading, simplifying our script.

In the rare situation that we need to run a system that has SSH exposed to the world, we always install something to monitor and block SSH attempts. On the low end, we use fail2ban or sshguard, but our preference is OSSEC – especially when protecting multiple systems.

The noise generated by SSH scanning is highly annoying – particularly if you’ve set these systems up to email on login failures.  We’ve used port-knocking before, but found it cumbersome to use.  We recently started using GeoIP blocking on some systems to cut scanning down to almost zero.  Since we’re in the United States and aren’t likely to try to access servers from other countries, we simply drop all traffic to port 22 where the address doesn’t map back to the United States.

It’s relatively easy to configure using Xtables and MaxMind’s free GeoIP databases.

1. Install xtables-addons

xtables is available in most distro repositories these days, which is a blessing.

2. Install the following script in /etc/cron.weekly and make it executable

#!/bin/sh
TMPDIR=$(mktemp -d /tmp/geoipupdate.XXXXXXXXXX)
mkdir -p /usr/share/xt_geoip
pushd ${TMPDIR}
/usr/libexec/xtables-addons/xt_geoip_dl
cd GeoLite2*
/usr/share/doc/xtables-addons-2.3/geoip/xt_geoip_build -D /usr/share/xt_geoip
[ -d "${TMPDIR}" ] && rm -rf ${TMPDIR}

Note: paths likely vary by distro!

3. Run the script manually to both verify it works and setup the Xtables database

2014-02-06 08:56:10 URL:http://geolite.maxmind.com/download/geoip/database/GeoIPv6.csv.gz [766825/766825] -> "/tmp/geoipupdate.AV0CTQk98X/GeoIPv6.csv.gz" [1]
2014-02-06 08:56:11 URL:http://geolite.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip [1293629/1293629] -> "/tmp/geoipupdate.AV0CTQk98X/GeoIPCountryCSV.zip" [1]
Archive:  /tmp/geoipupdate.AV0CTQk98X/GeoIPCountryCSV.zip
  inflating: /tmp/geoipupdate.AV0CTQk98X/GeoIPCountryWhois.csv  
101758 entries total
    0 IPv6 ranges for A1 Anonymous Proxy
   97 IPv4 ranges for A1 Anonymous Proxy
    0 IPv6 ranges for A2 Satellite Provider
  365 IPv4 ranges for A2 Satellite Provider
    1 IPv6 ranges for AD Andorra
...
   97 IPv6 ranges for ZA South Africa
  480 IPv4 ranges for ZA South Africa
   11 IPv6 ranges for ZM Zambia
   40 IPv4 ranges for ZM Zambia
    9 IPv6 ranges for ZW Zimbabwe
   46 IPv4 ranges for ZW Zimbabwe

4. Configure iptables to use geoip blocking

Be careful! You can easily lock yourself out of the system!

You might wish to consider inserting a temporary rule explicitly allowing traffic from your current IP address ahead of the GeoIP rules:

-A RH-Firewall-1-INPUT -s X.X.X.X -m tcp -p tcp --dport 22 -j ACCEPT

Here’s a simple rule to only allow US traffic to port 22. Note this only works if you’re using default deny, which of course, you should be using.

-A RH-Firewall-1-INPUT -m state --state NEW -m geoip --src-cc US -m tcp -p tcp --dport 22 -j ACCEPT

Here’s a different take. Instead of only permitting US traffic to port 22, it blocks non-US traffic to port 22. I only use this so I can see the rule counters and see how much traffic it’s blocking. However, if you’re not running default deny (i.e., you need to hire someone to look at your system configuration) this is what you’d need to use instead of the above rule.

Note that you do not need both rules, just one or the other.

-A RH-Firewall-1-INPUT -m state --state NEW -m geoip ! --src-cc US -m tcp -p tcp --dport 22 -j DROP

5. Restart iptables and test

You should find that your SSH scan attempts drop significantly. On systems we can restrict to a single country, we almost never see any SSH attempts.

Obviously this technique works on other services besides SSH. Experiment and enjoy.

Please comment below if you find this helpful or if you have criticism or suggestions!