I was looking for an easy way to geo block IP addresses with iptables or some other mechanism on Linux machines.  I got lucky and found a post online of someone looking for the same thing.  Like the majority of my posts it’s either something someone hasn’t figured out or something that isn’t quite right (not that I’m perfect).  I like using /var/ftp as a “working directory” so here are the steps.

Update!  For you lazy people I’ve PERFECTED a script ;-)

Here it is – one WARNING – the ONLY thing you need to do and I let you do this is turn on ufw, it may or may not need that I’m not 100% certain but I always have ufw on my servers running with exceptions for things like smtp, ssh, etc.  Run this to see if you’re using it:  ufw status.  Turn it on: ufw enable.  Make exceptions:  ufw allow 25, etc.  Help:  ufw –help.  I like the simplicity of UFW, you can even rate limit like so:  ufw limit OpenSSH.

So – here’s the script WITH current ipset blocks for every country except the US.  I do business in the US so I don’t block it.

Click here for the 7-zip file download:  http://rafaelwolf.com/sharedfiles/zoneblock.7z <– having problems making a link so for now past and copy!

Also – if you’re ssh’d to your server and you kick off the script it looks like it “locks up” but it’s not, WAIT, be patient!  What it’s doing is adding all the ipset rules and iptable information ;-)  You need to hurry up and wait!  What you’ll see next after the possibly long wait is the iptables save dialog (big pink screen).  THEN, make sure you turn your ufw firewall on – ENJOY!

I’d read the information below anyway JUST so you’re familiar with the process AND *** WARNING WARNING DANGER WILL ROBINSON *** do a “nano ipset.sh” on my script and read through that too.  It’s got notes of every process and step, what it does, etc.  It’s very well commented.  Again the only thing it doesn’t do is turn on ufw for you because that might cause some real problems.  Also – delete any of the countries you don’t want to block, each country is like cn.zone, us.zone, mx.zone, etc.  Just delete the countries you DON’T want to block and then run the script.  “sh ipset.sh” – then when it’s done, do a “tail -f /var/log/kern.log” on Ubuntu anyway and you SHOULD see the kernel BLOCKING any offenders.  It lists the blocked IP like so:

Jun 30 18:16:06 hostname.here kernel: [72323.443700] [UFW BLOCK] IN=eth0 OUT= MAC=00:0c:29:f5:86:ce:00:06:0d:4c:78:5d:08:00 SRC= DST=my.super.secret.ip LEN=40 TOS=0x00 PREC=0x00 TTL=110 ID=256 PROTO=TCP SPT=29986 DPT=8080 WINDOW=16384 RES=0x00 SYN URGP=0

Look up that IP with an IPwhoIS tool like http://whatismyip.com and you see that IP address is:   China Mobile Communications Corporation – guangxi

Good luck!

More reading on the how to below although some of it might be convoluted, it’s something I’ve come back to over time and progressively messed around with so some of it might not make sense but it’s still note worthy.

Step 1:  mkdir /var/ftp/zoneblock

Step 2:  cd /var/ftp/zoneblock

Step 3:  wget http://www.ipdeny.com/ipblocks/data/countries/all-zones.tar.gz

Step 4:  gunzip all-zones.tar.gz

Step 5:  tar -xvf all-zones.tar.gz

* Also look at the “rm -” commands in the file, you’ll want to run those FIRST before running the ipset.sh script

Step 6:  touch ipset.sh

Step 7:  Copy the below code into ipset.sh:  nano ipset.sh

# Update, all new and more automated!

# Remove the US zone, copyright and
# the tar file
# rm -f us.zone
# rm -f Copyrights.txt
# rm -f all-zones.tar.gz
# rm -f MD5SUM

# For each country in our zone directory
# add it to the country block list of
# ipset’s nethash
# for country in $(ls zone/ | grep zone)
for country in $(ls | grep zone)
# while read $country
do ipset -N $country nethash
# for IP in $(cat zone/$country)
for IP in $(cat $country)
do ipset add $country $IP
# for country in $(ls | grep zone)
do iptables -I INPUT -m set –match-set $country src -j REJECT

# Countries I used, block all but US

# If you want to list your rules run this command:
# ipset list geoblock
# * Warning, this WILL be a long list :-)
# You might be better off doing:  ipset list geoblock > geoblock_list.txt
# which will export your list into a text file for further review

# If you want to delete your list entirely runt his command:
# ipset destroy geoblock

Step 8:  If “screen” isn’t installed I recommend installing it, for ubuntu:  apt-get install screen, Cent or RedHat:  yum install screen

Step 9:  screen sh ipset.sh

Step 10:  Look at the above notes in the commented sections, notes start with a #, also note that if you COPY the syntax make sure it’s clean and nothing has skipped lines or gotten weird with the syntax on a line loop or line wrap.  Sometimes posting code on a blog doesn’t translate well because of the line wrapping.

You can confirm each country is indeed in the ipset rule respectively by this:  ipset list me.zone   or you could do any *.zone, au.zone, etc, etc.  You should see IP output!  If so they’re added to your ipset blocks!

I also added a few lines to the script recently, I didn’t realize you needed to leverage IPTables and “match” the ipset rules otherwise I believe ipset is useless!  It won’t work.  You’ll see then these added lines:

for country in $(ls | grep zone)
do iptables -I INPUT -m set –match-set $country src -j REJECT

Thanks to http://daemonkeeper.net/781/mass-blocking-ip-addresses-with-ipset/ discussion about ipset, I then went on the man page for ipset in Ubuntu:  http://manpages.ubuntu.com/manpages/lucid/man8/ipset.8.html and noticed:

“Iptables matches and targets referring to sets creates references, which protects the given sets in the kernel. A set cannot be removed (destroyed) while there is a single reference pointing to it.”

So you NEED to use an IPtable rule or a set of IPtable rules to match your ipset net hashes.  Without the IPtable rules ipset won’t work (I think :P).

If you have questions let me know – I worked on this for a solid hour I think…maybe more trying to get the syntax to work in my script!

Here are a few notes on how to navigate ipset with a few command examples to do xyz thing:

List sets:  ipset list -n

Save all sets:  ipset save

Save specific set:  ipset save cn.zone

Remove a specific set:  ipset flush ie.zone

Add an entry to a set:  ipset add cn.zone
* I had to add that set when they popped up on my radar from a denyhosts email I got – darn Chinese IP address trying to ssh into a server I keep watch over ;)