GeoIP Blocking Ubuntu 20.04LTS

This is an update to an earlier tutorial I wrote, GeoIP Blocking which worked for Ubuntu 16.04LTS and 18.04LTS, but apparently not with Ubuntu 20.04LTS.

The included version of the xt_geoip_build script in the new xtables-addons package has switched to using the GeoIP database from db-ip instead of MaxMind. This move simplifies the whole process and removes the need to sign up for a MaxMind account and use the work around to download and convert the MaxMind database.

For readability sake, most of the previous document is in here and I revised the portions that changed for Ubuntu 20.04LTS.

Install Prerequisites

sudo apt-get update; sudo apt-get -y upgrade
sudo apt-get install curl unzip perl
sudo apt-get install xtables-addons-common
sudo apt-get install libtext-csv-xs-perl libmoosex-types-netaddr-ip-perl

Getting Updates

sudo mkdir /usr/share/xt_geoip

Create a script to make it all work /usr/local/bin/geo-update.sh

#!/bin/bash

MON=$(date +"%m")
YR=$(date +"%Y")

wget https://download.db-ip.com/free/dbip-country-lite-${YR}-${MON}.csv.gz -O /usr/share/xt_geoip/dbip-country-lite.csv.gz
gunzip /usr/share/xt_geoip/dbip-country-lite.csv.gz
/usr/lib/xtables-addons/xt_geoip_build -D /usr/share/xt_geoip/ -S /usr/share/xt_geoip/
rm /usr/share/xt_geoip/dbip-country-lite.csv

Run the script and it will populate the database with GeoIP data for first use. Setup a cronjob to update the database regularly.

Validate It All Worked

Run:

modprobe xt_geoip
lsmod | grep ^xt_geoip

Output should look like:

xt_geoip               16384  2

Run:

iptables -m geoip -h

Output should look like (truncated):

iptables v1.6.1

Usage: iptables -[ACD] chain rule-specification [options]
       iptables -I chain [rulenum] rule-specification [options]
       iptables -R chain rulenum rule-specification [options]
       iptables -D chain rulenum [options]
       iptables -[LS] [chain [rulenum]] [options]
       iptables -[FZ] [chain] [options]
       iptables -[NX] chain
       iptables -E old-chain-name new-chain-name
       iptables -P chain target [options]
       iptables -h (print this help information)

Adding A Rule

This will not be persistent. But, it’s a way to see that things are okay. This example rule drops all traffic from RU and CN to port 25.

iptables -A INPUT -m geoip -p tcp --dport 25 --src-cc RU,CN -j DROP

At this point, any thing done above will not be persistent, so if you end up locking yourself out of your system, reboot it and you’ll be fine. Just make sure that you have access to a way to reboot your system or have access to a system console. Anything done below this can lock you out of your system permanantly so make sure you have a way to get into the system without having network access (console, ahem).

I use a different way of doing this since my system drops packets by default, I have rules that allow traffic based on geo. This example rule allows ssh traffic only from US-based IP addresses.

iptables -A INPUT -m geoip -p tcp --dport 22 --src-cc US -j ACCEPT

I had ufw running already (which is why the system is blocking all traffic by default), so integrating the GeoIP blocking with ufw was pretty easy and it’s a simple way of making rules persist over reboots. There are two files (one if your system is not IPv6) that need to be edited:

/etc/ufw/before.rules
/etc/ufw/before6.rules

As the naming suggests, the before6.rules file is for IPv6 and before.rules is for IPv4. Adding rules to these files is easy. Just add it before the COMMIT statement at the end of the file. An example of the ACCEPT rule above, add this like to the file(s):

-A ufw-before-input -m geoip -p tcp --dport 22 --src-cc US -j ACCEPT

If you plan to apply the same country rules to multiple ports, then it would look like this:

-A ufw-before-input -m geoip -p tcp -m multiport --dports 22,993,587 --src-cc US -j ACCEPT

That’s all!