0

So a few of the servers I manage are being slammed by traffic from China when the sites themselves would have utterly no appeal to anyone in China in any way. To make things worse, lots of unwanted brute force hacking and DDoS attempts are coming from China as well. So while I have Fail2Ban installed to block some of this traffic, I’d really like to just shut off any/all traffic from China—for the time being—to keep these servers running stably and happily.

I have read about how to enable GeoIP support for IPTables and have attempted to follow solutions such as the one presented here, but it doesn’t seem to work on my Ubuntu 12.04.5 (LTS) servers no matter what advice I seem to follow.

One choice I have is to bite the bullet and just upgrade these servers to Ubuntu 14.04, but that opens up a whole can of worms. Past any issues I am having with getting GeoIP filtering up and running, these servers are rock solid and perform quite well. An upgrade to 14.04 would just throw the specter of potential instability into them mix, and I have no time for that; the decision was made to stay on 12.04 since it’s stable and well supported until 2017 so I’m going to cross that bridge when I come to it.

So is there any way I can get IPTables on Ubuntu 12.04 to filter based on geolocation without having to jump through too many system package install hoops?

2 Answers 2

3

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.

3
  • 1
    wow. this answer would really deserve more upvotes. Feb 22, 2018 at 21:41
  • 1
    Yep. It also needs updating. They seem to have altered the CSV formats. I'll post a secondary answer.
    – Otheus
    Dec 7, 2023 at 22:21
  • @Otheus They no longer give out the data for free or in the same format. If you have a newer, more up-to-date process please feel free to post it. It would be good to see! Dec 7, 2023 at 22:39
1

Building on Giacomo's answer from 2015....

Now in 2023, MaxMind now requires you to go through a login process and generate a (free) license key in order to download the CSV files automatically. Currently, the permanent link for those looks like

https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country-CSV&license_key=YOUR_LICENSE_KEY&suffix=zip

You can create a cronjob to download this file once per day or once per week. Add the following script in /etc/cron.weekly for instance:

#!/bin/bash -e
CACHEDIR=/var/cache/geoip
read KEY << END_KEY
.... what you generate from MaxMind ...
END_KEY

install -d -m 0755 $CACHEDIR
cd $CACHEDIR
file=GeoLite2Country-CSV.zip
curl -s -S -L -o "$file" \
  "https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country-CSV&license_key=$KEY&suffix=zip"
unzip -j $file
rm $file

There are two sets of files: country-names separated by internationalization files, and the main geo-ip DB. I'll assume you want to identify the country name by its English spelling.

Documentation for these is found at https://dev.maxmind.com/geoip/docs/databases/city-and-country

To convert the CSV into an ipset, you can use this script, which can be appended to the previous one. The advantages here are efficiency and atomicity. The ipset restore operation (from Giacomo's answer) will accumulate changes and not remove ones that are no longer invalid.

This solution will be to swap the old out with the new, such that the iptables rules do not need to be removed, and the service won't be interrupted.

# Setup....
country_name=REPLACE_WITH_YOUR_COUNTRY_NAME
ipset=geo-$country
ipset_temp=$( mktemp -u $ipset.XXXXX )
ipset create -exist $ipset hash:net
ipset create $ipset_temp hash:net
trap "ipset destroy $ipset_temp" EXIT

# Extrapolate the country-code from the Country-Locations DB
ccode=$( awk -F, -v cname=$country_name '$6 == cname { print $1 }'  GeoLite2-Country-Locations-en.csv )

# Extrapolate and restore the IPs found in the IPV4 set.
awk -F, -v ipset=$ipset_temp -v ccode=$ccode '$2 == ccode || $4 == ccode { print "add",ipset,$1 }' GeoLite2-Country-Blocks-IPv4.csv  | ipset restore

ipset swap $ipset_temp $ipset

Please note: If you like my answer, also upvote Giacomo's, since he did most of the work.

A complete working script is published as a Snippet.

2
  • 1
    Nice work updating the answer for modern use! Dec 7, 2023 at 23:22
  • 1
    @Giacomo1968 check out the new snippet on gitlab
    – Otheus
    Dec 8, 2023 at 20:41

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .