How to use iptables in Ubuntu to block whole countries

firewall-iconTraffic to this blog has exploded. That’s the good news. The bad news is that most of the malicious traffic I am getting comes from just a few countries– places that monitoring shows spend little to no time looking at content.

This blog runs on Ubuntu in Microsoft Azure and I had been using the Azure firewall to permit only traffic on ports 22 (ssh), 80 (HTTP) and 443 (TLS). That was no longer a sufficient defense as hackers long ago gave up on port-scanning.

To cope, I started adding offending IP addresses, one at a time, to the Apache .htaccess file. This is simple and easy — one deny from and that IP address is blocked by Apache. But it’s not scalable. I can’t monitor the blog 24x7x365 for offending IP addresses and then add them to .htaccess manually.

So, I decided to attempt to block all IP addresses from specific countries. This turns out be relatively easy. As always, the Internet comes to the rescue with detailed how-tos. You will need to be familar with the built-in Linux firewall iptables. Some find iptables impenetrable but, like all technology, once you “get it” it’s easy. Here’s a good overview of iptables.

There are two basic steps to using iptables to block IP addresses by country:

  1. Install xtables-addons-commons and xt-geoip. Jeshurun’s Blog has the best write-up of how to do this. His instructions worked perfectly on my Ubuntu 14.04 Azure instance.
  2. Set up the iptables rules. There’s an excellent tutorial for that here by Ramesh Natarajan.

However, Ramesh assumes that default policy for the FILTER table is DROP. That’s not usually the case in cloud instances, which typically use the cloud provider’s firewalls and packet-level filtering.

In those types of instances, if iptables is running the INPUT table usually has a default policy of ACCEPT. Using Ramesh’s logic, you would drop all packets from all countries.

So, I re-worked his rules, shown here in iptables-save format (suitable, obviously, for piping into iptables-restore).

# Generated by iptables-save v1.4.21 on Wed Jun 22 14:49:18 2016
:INPUT ACCEPT [8052:1167532]
:OUTPUT ACCEPT [10056:6913879]
-A INPUT -m geoip --source-country YE  -j LOGGING
-A LOGGING -m limit --limit 2/min -j LOG --log-prefix "IPTables-Dropped: " --log-level 4
# Completed on Wed Jun 22 14:49:18 2016

The way this works is:

  • The INPUT table policy is defaulted to ACCEPT
  • A custom table, LOGGING, is added as described by Ramesh
  • A geoip rule is added to the INPUT table chain for each country code to be dropped and just those packets are sent to the LOGGING table.
  • Two rules are in the LOGGING table chain. The first logs the dropped packets to /var/log/kern.log (not /var/log/messages as Ramesh notes – this might be an Ubuntu difference from what he was using). The second does the real work: dropping the packet based on the ISO country code. Here, you can see I am dropping all packets from Yemen.

Eliminating traffic from additional countries is as simple as adding another rule to the INPUT table chain with a different two-letter country code that sends the packet to the LOGGING table where it will be logged and dropped.

Then, just sit back and watch the dropped packets with a tail -f /var/log/kern.log  I haven’t noticed any performance issues with this filtering running; iptables is part of the Linux kernel so overhead should be slight.

To my friends in Sana’a: sorry that a few bad apples forced me to do this (though you’ll never see this post).






Leave a Reply

Your email address will not be published. Required fields are marked *