Country Blocking with iptables and ipset

The purpose of this howto is to outline a a script which can be used to create a list of country-associated IP's which can be used to either block or allow access to your system. There are 4 part elements to this:

  1. Create the country list
  2. Set up a boot mechanism
  3. Create the firewall rules

Create the country list

The following script will create an ipset list of selected countries' IP addresses. Create a file, /etc/cron.monthly/country_list in in it put:


# A list of the ISO country codes can be found at
# Countries are case insensitive for this script

ISO="at be ch cy cz de dk es fr gb gr ie it lu mt nl pt eu va sm mc je gg im"

if [ "`lsmod | grep ip_set`" = "" ]; then
	modprobe ip_set

# Destroy country-list-temp in case it exists and is populated
ipset destroy -q country-list-temp

# Make sure the new lists exist
ipset create country-list nethash maxelem $MAXELEM -exist
ipset create country-list-temp nethash maxelem $MAXELEM -exist

# Load the country list
curl -s -d country=1 --data-urlencode "country_list=$ISO" -d format_template=prefix | grep -v ^# | while read -r line
    ipset -A -exist country-list-temp $line

if [ $(ipset list country-list-temp | wc -l) -le 7 ]; then
    logger -t country-list "Update failed"
    echo 'Country List Update failed' | mail -s 'Country List Update failed'
    ipset destroy -q country-list-temp

# Make the temp list current
ipset swap country-list country-list-temp

# Destroy the (now old) temp list
ipset destroy -q country-list-temp

# add some exceptions
#ipset add -exist country-list
#ipset add -exist country-list
#ipset add -exist country-list

# Create save list for loading on boot
ipset save country-list > /usr/src/
sed -i 's/create/create -exist/g' /usr/src/
sed -i 's/add/add -exist/g' /usr/src/

logger -t country-list "Updated"


  • you could put it in cron.weekly if you wanted but the list should be fairly stable so monthly updates are probably OK.
  • change the list of countries to suit your needs. A full list of country codes can be found here .
  • change the e-mail address to suit where you want the error message sent to if the update fails.

Then make the script executable and execute the script for the first time:

chmod 0755 /etc/cron.monthly/country_list

It takes a couple of minutes to run. If it errors, you may need to increase the list size (MAXELEM). China and the US both produce very big lists. Oddly, increasing the number of countries blocked can reduce the list size. This can happen as it can become possible for lots of smaller IP blocks to be consolidated into fewer bigger IP blocks. does this consolidation for you.

Boot up

In order for this to work from start up, we need to restore the last created list that the script backs up, otherwise you have to wait until the next cron.monthly run. Add the following to /etc/rc.d/rc.local:

# Load in all previously saved ipset sets
if [ "`lsmod | grep ip_set`" = "" ]; then
	modprobe ip_set

for file in /usr/src/ipset_*.save ; do
	ipset restore <  $file

And make it executable:

chmod 0744 /etc/rc.d/rc.local

Add the firewall rules

Note the firewall rules need to be personalised to your environment. Add them to a file /etc/clearos/firewall.d/20-ipset-blocks. This could be something like:

# IPv4 only for now

if [ "$FW_PROTO" != "ipv4" ]; then
    return 0

if [ "`lsmod | grep ip_set`" = "" ]; then
	modprobe ip_set

# Block country addresses (exempt permitted countries)
# note the  > /dev/null 2>&1 is needed for some odd reason
ipset create country-list nethash -exist  > /dev/null 2>&1
$IPTABLES -I INPUT -m conntrack --ctstate NEW -m set ! --match-set country-list src -p tcp -m multiport --dports 587,993 -j DROP
$IPTABLES -I INPUT -m conntrack --ctstate NEW -m set ! --match-set country-list src -p udp -m multiport --dports 1194 -j DROP

This example would restrict you to only be able to pick up e-mails and connect to OpenVPN from your chosen countries as these ports are open further down the INPUT chain.

A rule to block anything but TCP would be:

$IPTABLES -I INPUT -m conntrack --ctstate NEW -m set ! --match-set country-list src ! -p tcp -j DROP

As an alternative to allowing OpenVPN in the normal incoming firewall then dripping OpenVPN from outside the chosen list, you could not open OpenVPN in the incoming firewall and just use:

$IPTABLES -I INPUT -m conntrack --ctstate NEW -m set ! --match-set country-list src -p udp -m multiport --dports 1194 -j ACCEPT

Once you have created your firewall rules, restart the firewall with a:

systemctl restart firewall
All the main part of the script does is assemble a list of country IP's. The firewall can then be used to to only allow this list as has been done in these examples, or, by writing the rules differently, it can be used to expressly block this list.


content/en_us/kb_howtos_country_blocking.txt · Last modified: 2020/04/18 07:43 by