We have posted about blocking countries accessing servers via the GeoIP module from Xtables-addons. However, since that method relies on 3rd party kernel, some users have trouble using it.
So in this post, we are going to show you how you can block access with Ipset, which is already included in the Linux kernel, so we don’t have to rely on 3rd party kernel, and we are also going to use the IPs blacklist ipset format maintain by Firehols:
As you can see, they actively update those ipset regularly, and those sets are not limited by countries only. There are lists for continents, datacenters, known proxies/VPN, malicious IPs, etc. You can use any list you want and block access from IP matching in those lists.
Prerequisites
First, we will install the dependencies, then make a directory to which we will download those ipset, then download a bash script that helps us load the downloaded ipset into the kernel.
# Install dependencies
sudo apt install ipset iprange netfilter-persistent
# Make a dir and go into it
mkdir blocklist
cd blocklist/
# Download bash script use for loading ipset into kernel, and make it executable
wget https://raw.githubusercontent.com/firehol/firehol/master/contrib/ipset-apply.sh
sudo chmod +x ipset-apply.sh
The ipset-apply.sh script can help us apply all ipset found in the directory, but first, we need to change the base directory defined in the script to make sure the script knows where to look for.
Next, we will download the ipset we want to use for filtering traffic. In this post, we’ll assume we want to block IPs from Russia and data centers. You can use any list you want in your case. Then we’ll load those download lists into the kernel with the script.
# Change the base directory of ipset-apply.sh, use pwd to check current dir, then nano modify the script.
pwd
nano ipset-apply.sh
# We'll use this country_ru list based on geolite2 and datacenters
wget https://raw.githubusercontent.com/firehol/blocklist-ipsets/raw/master/geolite2_country/country_ru.netset
wget https://raw.githubusercontent.com/firehol/blocklist-ipsets/raw/master/datacenters.netset
# Use the script to load ipset into kernel
./ipset-apply.sh country_ru.netset
./ipset-apply.sh datacenters.netset
If you have done everything correctly, this is what it will look like:
Using Ipset in Iptables Firewall
Now that we have loaded country_ru and datacenters into the kernel, we are ready to use these two set with Iptables, this is how we add them into Iptables:
iptables -I INPUT -m set --match-set country_ru src -j DROP
iptables -I INPUT -m set --match-set datacenters src -j DROP
This is what it looks like after adding them to Iptables.
Since we make this guide on a newly created AWS Lightsail instance, there are no other rules in the INPUT chain. However, if you have other rules, please note that adding rules without specifying the number will add to the first of the chain.
For example, if you have some whitelisted IP from data centers that you want to allow, and they are at the first before you add those filters, you can specify the number when adding rules like so:
iptables -I INPUT 2 -m set --match-set country_ru src -j DROP
iptables -I INPUT 3 -m set --match-set datacenters src -j DROP
By doing so, you can have whatever match your first rule to skip going through the rest of the rules and getting blocked.
Block specific port with ipset
If you host multiple applications on your server, for example, a blog and a Minecraft server, and you only want to block IPs that match those Ipset accessing your Minecraft server, then you can specify the port when adding the rules.
iptables -I INPUT -m set --match-set country_ru src -p tcp --dport 25565 -j DROP
iptables -I INPUT -m set --match-set datacenters src -p tcp --dport 25565 -j DROP
Block IPs that are not included in ipset
You can do it reversely by only allowing IPs included in the ipset to access and block all other IPs. For example, to allow only IP from Russia to access your Minecraft server:
iptables -I INPUT -m set ! --match-set country_ru src -p tcp --dport 25565 -j DROP
Logging access with a prefix
If you want to keep logging for IPs being blocked by your rules, just add the logging rules in front of the rule you drop their connection.
iptables -I INPUT -m set --match-set country_ru src -p tcp --dport 25565 -j LOG --log-prefix "[BLOCKED COUNTRIES] "
Making the Iptables persistent on reboot
Now we will have to ensure our server automatically applies those rulesets after reboot. We do that by using netfilter-persistent. However, by default, it doesn’t save the ipset, so we would also use a small plugin for netfilter-persistent to make our ipset persistent.
# Download the ipset plugin for netfilter-persistent
wget https://github.com/freeyoung/netfilter-persistent-plugin-ipset/raw/master/10-ipset
chmod +x 10-ipset
mv 10-ipset /usr/share/netfilter-persistent/plugins.d/10-ipset
# Now make the iptables and ipset persistent
sudo netfilter-persistent save
Updating Ipset regularly with a simple script
Now it is also a good idea to keep the list updated, there are various ways to do this, but to keep this post short, we will just make a script that will download those ipset and load them into the kernel, then make it persistent.
First, create a script update.sh then copy the below content into it and make it executable:
#!/usr/bin/bash
BASE="/home/ubuntu/graphite"
cd ${BASE}
# Download and update list
curl -sO https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/datacenters.netset
# Load to kernel
./ipset-apply datacenters.netset
# Make persistent
netfilter-persistent save
Then set up a crontab and run this script in any frequency you like to keep your ipset updated, then you are done!
If you have any issues while following this guide, feel free to comment below, we will check and reply periodically. If this post helps you, please consider sharing!
Are you really sure that netfilter-persistent is enough for making the iptable roules persistent? In my case they only saved the ipsets and loaded them back into the kernel but not created the ipfilter rules. With ipfilter-persistent I got it running
This only works till the server is rebooted. Once the server reboots, you have to run the ipset-apply.sh again in order to load/reload the ip set into the kernel. Using iptables-persistent will produce an error on the line trying to restore the iptables match set (since they are not loaded in the kernel) .
-A INPUT -m set –match-set datacenters src -j DROP
will not work until you re-run
sudo ./ipset-apply.sh datacenters