Friday, September 9, 2011

GeoIP Filtering on Ubuntu 11.04 (Natty Narwhal)

Ubuntu 11.04 gets you about half way there, this guide will take you the rest of the way.

Install and Test Kernel Module
What would normally be the most time consuming portion has been taken care of for us by the Ubuntu package maintainers.  We need to install xtables-addons, which will build, among other things, the GeoIP module for use with iptables.  You will also need unzip for dealing with the country code data we will download later and the kernel source matching your running kernel.  Without the kernel source, xtables-addons-dkms package will not automatically build modules for us.
sudo apt-get install linux-headers-XXX unzip xtables-addons-common
That will also pull in xtables-addons-dkms as a dependency.  If all goes well, the kernel modules will be automatically built immediately after installation.  You should see xt_geoip.ko scroll by; that's the one we are interested in now. 

Test the module now with a quick iptables command.  At this point, the command will fail, but the output will still be useful to us.  On the off chance that you have the GeoIP database already installed, I would recommend not using a port that you are currently using to access your Linux box, just in case.
sudo iptables -A INPUT -p tcp --dport 80 -m geoip ! --src-cc US -j DROP
This is what you want to see.  It means that the geoip module loaded and is looking for a file in a statically defined location.
Could not open /usr/share/xt_geoip/LE/US.iv0: No such file or directory
If you see an error like "Couldn't load match 'geoip'", it means that iptables was not able to load the geoip module, and that means that xtables-addons was not able to build them.  The most likely cause of that is missing or incorrect kernel headers.

If you don't get any errors at all, it means the command succeeded and you now have a geoip filtering rule in your firewall set.  You're done, go outside and play.

Install GeoIP Database
This is where Ubuntu 11.04 leaves you;  you've got the module installed and working, but without a database, it is worthless.  Ubuntu does offer other GeoIP packages, like geoip-bin and geoip-database.  Of those, the database one looks promising on the surface, but it won't help us with filtering.  Package geoip-database installs an IPv4 and IPv6 databases in /usr/share/GeoIP, but they are not in a format usable by the GeoIP filter kernel module.

To install a usable database, go the the xtables-addons home page on SourceForge and download the latest tar ball.  The latest at the time of this writing is 1.38.
wget http://sourceforge.net/projects/xtables-addons/files/Xtables-addons/1.38/xtables-addons-1.38.tar.xz
Create the required directory structure:
sudo mkdir -p /usr/share/xt_geoip
There are only two files we need out of the tarball, a couple of perl scripts: xt_geoip_dl and xt_geoip_build. The former downloads zipped CSV country codes while the latter converts the CSV data into a binary database format for faster lookups. Extract them and move them to /usr/share/xt_geoip.

Run the download script first:
cd /usr/share/xt_geoip
sudo ./xt_geoip_dl
Before running the build script, there are two additional steps required.  First is the installation of a perl module that xt_geoip_dl requires.  This module was not installed by default in my Ubuntu build.
sudo apt-get install libtext-csv-xs-perl
That was the only dependency I was missing, but your mileage may vary.
Next, we need to tweak the script a little.  The geoip kernel module will be looking for files named <CountryCode>.iv0, but the build script creates <CountryCode>.iv[4,6].  This discrepancy, I believe, is caused by a downlevel geoip module included in Ubuntu 11.04's xtable-addons package.  You could solve this problem another way than I show here; for instance you can build the geoip module from source using the tar file downloaded earlier.  The following method, however, is quicker and it works fine for me.

Edit xt_geoip_build and locate the line '$file = "$target_dir/LE/".uc($iso_code).".iv4";'.  In 1.38, it is line 106.  Change the ".iv4" part into ".iv0" and save the file.
From this:
106   $file = "$target_dir/LE/".uc($iso_code).".iv4";
107   if (!open($fh_le, "> $file")) {
108     print STDERR "Error opening $file: $!\n";
109     exit 1;
110   }
To this:
106   $file = "$target_dir/LE/".uc($iso_code).".iv0";
107   if (!open($fh_le, "> $file")) {
108     print STDERR "Error opening $file: $!\n";
109     exit 1;
110   }
Now we can run the build script.  The following will convert the CSV data into binary and store the result in the current directory, which should still be /usr/share/xt_geoip.
sudo ./xt_geoip_build -D . *.csv
Two new directories are now created, BE and LE, and they are full of country code files.  You might notice that we did not change the extension of the files in the BE directory.  I don't believe this is necessary, because the xtables-addons geoip package only uses the LE files (which I think stands for Limited Edition, which is the freebie stuff offered by the kind folks at MaxMind).

Now for a real test; this time we are hoping for success!  Try the previous iptables command again, and if all goes well, you will be greeted with blessed nothingness:
sudo iptables -A INPUT -p tcp --dport 80 -m geoip ! --src-cc US -j DROP
 If there were no errors, you can double-check that the add was successful by running this:
sudo iptables -L
Hopefully you will see a line similar to the following in your INPUT chain:
DROP       tcp  --  anywhere             anywhere            tcp dpt:www Source country: ! US
To remove the rule, simply replace the add command with a delete (-A to -D):
sudo iptables -D INPUT -p tcp --dport 80 -m geoip ! --src-cc US -j DROP
All pau.