Country-based packet filtering with iptables

Posted by imagine on Tue 17 Apr 2007 at 17:59

Bruteforce attacks shouldn't pose a real security risk to any server but are still annoying and clog up your logfiles. Many methods to block these break-in attempts exist, like BlockHosts, Fail2ban or rate-limiting incoming connections. However, on my search I also came across one tool for which I couldn't find an easy guide: geoip. geoip is a module for netfilter/iptables and allows you to filter packets based on the country they come from or go to. Following is a step-by-step guide on how to install geoip.

Obviously the module can also be used for other things than just blocking bruteforce attacks. For example for what the original author calls "racist routing", that is to prevent users from certain countries from accessing your webserver or whatever. But as always it's up to everyone individually what he uses it for.

First off let me say that I don't administrate servers for a living but only do this in my spare time, so be warned. If you find any errors or wrongdoings please correct them in the comments.

I'll use Debian 4.0 in this howto, but in principle it should work with most others GNU/Linux distribution too. I also tried it on Ubuntu 6.10, where it works with very minor changes. Of course you'll have to use something else than apt-get on non-Debian systems, but people who spend their time setting up iptables-rules will probably figure this out for themselves.
What we'll need for geoip to work is a new kernel module, a library for iptables and a file with the IP-to-country mapping. There will be no kernel recompilation.

1. Preparations

First get the sources of the kernel you use. If you use the standard kernel of Etch that currently is:

# apt-get install linux-source-2.6.18
apt-get will put a compressed archive containing the source code in /usr/src/, which we will extract:
# tar xjf /usr/src/linux-source-<VERSION>.tar.bz2 -C /usr/src/
Replace <VERSION> by the version number of your kernel, in Etch it's currently 2.6.18. Since the source code is about 300MB big, this will take some time.

Next get the source code of iptables. We won't recompile iptables, but we need the sources to build the necessary library. We'll create a temporary directory to work in:

# mkdir ~/geoip
# cd ~/geoip/
# apt-get source iptables
You must have the dpkg-dev package installed to use the last command.

Now only the geoip-files are missing:

# wget http://people.netfilter.org/peejix/patchlets/geoip.tar.gz
To apply them we need the netfilter tool patch-o-matic-ng from http://ftp.netfilter.org/pub/patch-o-matic-ng/snapshot/. If you use a very old kernel, you may have to pick an older version, but other than that the newest snapshot is fine. At the moment that is:
# wget http://ftp.netfilter.org/pub/patch-o-matic-ng/snapshot/patch-o-matic-ng-20070414.tar.bz2
Extract patch-o-matic-ng:
# tar xjf patch-o-matic-ng-20070414.tar.bz2
Then extract the geoip-files into a subfolder of the new patch-o-matic-ng-directory:
# tar xzf geoip.tar.gz -C patch-o-matic-ng-20070414/patchlets/

2. Patching

Patch the kernel and the iptables source code with patch-o-matic-ng:

# cd patch-o-matic-ng-20070407/
# KERNEL_DIR=/usr/src/linux-source-<VERSION>/ IPTABLES_DIR=~/geoip/iptables-<VERSION>/iptables/ ./runme geoip
Again, replace both <VERSION>-tags with the appropriate versions of your kernel and iptables. At the moment of this writing the version of iptables in Etch is 1.3.6.0debian1.
If everything's right, patch-o-matic-ng will present you an explanation of the geoip-patch and ask if you want to apply it. Answer y, patch-o-matic-ng will then print a success message and exit.

3. Building and installing the geoip kernel module

To build, we need the build-essential package or anything similar which will provide us with gcc, make, etc:

# apt-get install build-essential
Because we need a configuration file for the kernel, we will just use the configuration of the currently running kernel:
# cd /usr/src/linux-source-<VERSION>/
# make oldconfig
During the configuation process make will ask:
geoip match support (IP_NF_MATCH_GEOIP) [N/m/?] (NEW)
Type m to use it as a module.

Prepare the build process of the kernel modules:

# make modules_prepare
Then compile the modules. Unfortunately I don't know how to compile only the geoip module, so we'll have to compile all netfilter modules until someone has a better idea:
# make -C $(pwd) M=net/ipv4/netfilter/ modules

If all went well, ipt_geoip.ko should have been created in net/ipv4/netfilter/. We copy it to the other modules of our current kernel:

# cp net/ipv4/netfilter/ipt_geoip.ko /lib/modules/$(uname -r)/kernel/net/ipv4/netfilter/
Activate the new module:
# depmod
# modprobe ipt_geoip
# echo "ipt_geoip" >> /etc/modules

4. Building and installing the iptables library

Build the shared object:

# cd ~/geoip/iptables-<VERSION>/iptables/
make KERNEL_DIR=/usr/src/linux-source-<VERSION>/  extensions/libipt_geoip.so
As before, substitute <VERSION> with the version of iptables and the kernel.

Copy the created file to the other iptables libraries:

cp extensions/libipt_geoip.so /lib/iptables/

5. Creating the IP-country mapping

We will use the freely available database from MaxMind. It has a claimed accuracy of 98% and is updated once a month. If that's not enough, you can buy a more accurate version from MaxMind.
Download the file and extract it:

# cd ~/geoip/
# wget http://www.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip
# unzip GeoIPCountryCSV.zip
As you can check with an editor, the extracted file is a list of comma separated values. Since seeking through such a list is pretty slow, we will convert the file into binary format using csv2bin, a tool also supplied by the author of geoip.
# wget http://people.netfilter.org/peejix/geoip/tools/csv2bin-20041103.tar.gz
# tar xzf csv2bin-20041103.tar.gz
# cd csv2bin/
# make
# ./csv2bin ../GeoIPCountryWhois.csv
That will create two files, geoipdb.bin and geoipdb.idx, which the geoip module expects in /var/geoip.
# mkdir /var/geoip
# mv geoipdb.* /var/geoip/

6. Using iptables with geoip support

That was it, you can now filter packets on a country basis:

# # Block access to your SSH daemon except from Afghanistan
# iptables -A INPUT -p tcp --dport 22 -m geoip ! --src-cc AF -j REJECT
# # Block pinging hosts in France, Italy, Spain
# iptables -A OUTPUT -p icmp -m geoip --dst-cc FR,IT,ES -j REJECT
# # Help
# iptables -m geoip --help

If you want, you can delete the ~/geoip/ and /usr/src/linux-source-<VERSION> directories now.


This article can be found online at the Debian Administration website at the following bookmarkable URL:

This article is copyright 2007 imagine - please ask for permission to republish or translate.