Turns out I don’t have to completely reinvent the wheel to get some sort of GeoIP support mixed in with IPTables. Just using IPSet with a copy of MaxMind’s GeoIP Country database in CSV format—in addition to one simple IPTables rule—is all you need to get this up and running in Ubuntu 12.04.5 (LTS).
A side note, but some online tutorials that explain a procedure like this recommend using web-based country zone files like the ones that come from IPDeny’s website:
http://www.ipdeny.com/ipblocks/data/countries/cn.zone
While this technically works, I don’t like the idea of having to be so reliant on an external website’s data like this. What if I want to create an automated scripting process to manage this and the IPDeny site goes down or is hacked? Whose IPs would I be blocking.
That’s why I am preferring to use the MaxMind GeoIP Country database in CSV format on my servers. I can always fetch a new copy of that database if I need updates and even if their site is down I always have a copy of the database on my servers. And since that database contains all the countries in the world, I can always easily add more countries to the IP set by using the country’s two letter ISO 3166-1 country code.
Anyway, here are the steps I took to get this done with IPSet and the MaxMind GeoIP Country database.
1. Install IPSet.
First, install IPSet like this.
sudo aptitude install ipset
Once that is installed, create a BANNED_RANGES
IP set like this:
sudo ipset create BANNED_RANGES hash:net
2. Get a copy of the MaxMind GeoIP Country database in CSV format.
The next key to this is to get a copy of the MaxMind GeoIP Country database in CSV format installed on the server. My steps are as follows:
curl -O -L http://geolite.maxmind.com/download/geoip/database/GeoIPCountryCSV.zip
Now just UNZip that archive:
unzip -o -q -d . GeoIPCountryCSV.zip
3. Filter and import a country specific IPSet config like this.
Now we will use Awk to filter out the China specific IP ranges into an IPSet config file:
awk -F "," -v COUNTRY_CODE=CN -v IPSET_TABLE=BANNED_RANGES} '$5 ~ COUNTRY_CODE { gsub(/"/, "", $1); gsub(/"/, "", $2); print "add "IPSET_TABLE" "$1"-"$2; }' /usr/local/share/GeoIP/GeoIPCountryWhois.csv >> ipset.BANNED_RANGES.conf
That will create an IPSet config file named ipset.BANNED_RANGES.conf
that can then be imported into IPSet like this:
sudo ipset restore < ipset.BANNED_RANGES.conf
And you can then check the items in that set with this command:
sudo ipset -l BANNED_RANGES | more
4. Make IPTables aware of the IPSet.
Now the final step that brings it all together is to insert a simple rule into IPTables like this:
sudo iptables -I INPUT -p tcp -m set --match-set BANNED_RANGES src -j REJECT
Once that’s done, IPTables now knows that any IP addresses or ranges added to the BANNED_RANGES
set will simply be rejected via REJECT
.
If you somehow want to get rid of that rule later on you can run this command:
sudo iptables -D INPUT -p tcp -m set --match-set BANNED_RANGES src -j REJECT
But to me that’s a tad messy. A cleaner way to handle a case like this is to just flush all of the IP data out of the BANNED_RANGES
set like this:
sudo ipset flush BANNED_RANGES
By doing this you can have that IPTables rule in place and not have to do anything else but flush the data out of BANNED_RANGES
. Makes it nicer/easier to update the addresses or ranges if you want to ever add or remove IP addresses or ranges from that set.