DGhost's Blog

A sysadmin thoughts about the Internet and technologies…

Banning an entire country with IPTables/IPSet

| 32 Comments

A couple of years ago I would have been shocked with this simple idea. To ban an entire country from ever using the service of one of my public hosted server. I would have never proposed or even agreed to an idea like this. This was a longtime ago and now the landscape of the Internet has changed so much that I’ve been resolved to use this simple, yet so effective, solution. I mean, this goes against the basic nature of the existence of the Internet; Information wants to be free and it should be available to anyone who wants to access it, anywhere on this world. And yet, here I am today, banning whole countries forever reaching the services of web and email services on some of the servers that I managed. Why? Because I’m tired of some organizations abusing servers. I am tired of constantly checking the status of the networks I must look after. I am tired of seeing that almost 99% of time, the attacks are always coming from the same countries. So after many years of consideration, in the autumn of 2013, I finally gave in and I’ve started banning entire countries with iptables. After many months of using it, the only conclusion that I have is, why I haven’t done that before?

The technical requirement and configuration are really easy, we will go trough it very soon, but before doing so, let’s take a step back to reflect on why we would like to do it and what are we going to achieve with that kind of setup. I’m managing and monitoring a lot of different kind of servers. Most of them are private and a few of them are public. They all have some different kind of security layers, but the public ones need some special care and more careful monitoring. Obvious reason; these are servers offering services to everyone on the internet with no special security access to reach it. Mostly web pages, emails, FTP and database access. Sometimes they have special services, but they are usually the exception, not the norm. Of course, some of the basic stuff for protecting the services are already configured, using keys for SSH login, password complexity; for making sure the users have good password (note to self ; make sure not to use the same password for my luggage and for the shield of planet Druidia), fail2ban and/or WAF to ban the IPs for the external attack,  OSSEC for the internal attack, etc. But even with all of these different layers of protection, some problem might still arise. Like an old version of a WordPress being hosted and never updated or a plugin of whatever web platform that has a big security hole and was never patched. External/public servers are a very different kind of servers because for the most simple reason, security. You have to know exactly what you are getting into when you setup a public server. Why is that? Because there is always someone, somewhere, trying to break into the server and take control of it. The reasons for it are not that important, it might be for launching a DDOS attack, making it a FTP/Torrent server for Warez, hosting banners for pushing viruses or some new kind of phishing/spamming/scheming or simply just for the fun of it. 14 years ago it was pretty easy to setup a public server because there was rarely an attack on it. Now, today, it’s happening every second, if not 10 times per second. Brute force password are being constantly done against all public servers on the Internet. You might have changed the default port of your SSH service from 22 to whatever else, you are only avoiding the script kiddies and not the professional ones – who are the most dangerous/problematic. And even tough you are using many different kind of layers for protecting your server, you are truly never safe against some specific attack like DDOS, unless you have the money for buying an extra powerful firewall or are using the specialized service like Black Lotus.

Where am I getting at? It’s now officially a pain in the ass / full time job to monitor a public server for security. And you know what? Most of theses attacks are, mostly, always coming from a specific country. By simply using fail2ban and getting the emails reports when a brute force password is done, you can know exactly from which country the IP that has done it is coming from. Take this page for example. One of the old version of fail2ban had in it’s default configuration file, as an example, for reporting emails to fail2ban@mail.com. The original author probably thought that this didn’t existed, but alas, yes it does exist! Somebody had the extraordinary idea of registering that email address and he has been recording the emails that are sent to it for statistics analysis. On that web page you can check what countries are launching the most attack for brute force password and what countries are the most targeted by it. Another service you might want to check is www.blocklist.de – a fail2ban reporting service. A lot of fail2ban users have registered an account with this service and are sending them the information of whoever (IP) are currently attacking them. You can download their master list and add it to your firewall.

But after all of this being said, if you take the time to think about it, it’s a constant-never ending wheel of security. I’ve started to think about this idea of banning an entire country more than 3 years ago. I knew it could be “easily” done because of the simple feature that we can use today, geo IP location. All IPs are given in specific and, most of the time, contiguous block to countries and it’s registered to numerous database, available for free, on the web. So, I started thinking, who among my users are located in other countries? If the servers I’m looking for are all located in North America, I can easily check that all the users on it are located in America also. If that server has only less than 100 hundred users, it can be easily check with the logs of the different services. Of course, I also had to warn the users of the servers of my intention. Like banning one specific country because 97% of all the brute force attacks that were done against all the services on that server were all coming from that country. So after explaining that to all the users, I can also offer them some exception, if they where ever traveling to that country, they could send me an email from an email account not belonging to my server, like using their gmail or hotmail account and giving me the public address of where they could be located so that I can push an exception in the firewall. Thankfully, this kind of scenario has never happened so far.

So what do we need? a firewall for sure. Iptables in this case and Ipset with that. Why? Because of the way iptables works. When a connection is made to a linux server with iptables running on it, you have to know, that iptables take the IP and compare it to its internal list. line by line. Now imagine that you have an iptables list made up of thousand of lines. Do you think it will slow down the connection process? You bet. If your list has many entries, like in the range of hundreds of different IPs and some of them are block of IPs, using only Iptables is not efficient. That’s why you need Ipset. I highly recommend that you learn how Ipset work if you intend to ban an entire country. Ipset need iptables to work and is rarely installed by default on a Linux server. For this example, I’m using CentOS 6.5 and for installing ipset it’s a simple matter of typing :

# yum -y install ipset

There you go, it’s done. Since ipset is in the base repo, I don’t even need to connect to a third party repo to have it installed. libmnl should be installed automatically with that for dependency. For other distro the command may be different but the idea is the same. Now for the next part, if you have ever done a search on Google (or any other web search engine) about the term “iptables ban country” you will quickly realize that there are many ways on how to do it. Most of them all are good. The trick is to know where you get the “master list” for the IPs belonging to a specific country. Many websites offer that information but not all of them are up to date. There are 2 different main way to do this. First one is to connect to a service with some tools on Linux for getting a “live” list of Geo IP or the second way is to download a list from a webpage and putting in a text file on your server. You’ll quickly think that the first way should be better because the query is done on another server and you’ll always have an up to date list. And yet, this is where you will probably have the most problem. Most of these external servers are providing these service free of charge and are not always online, meaning that it will stop your iptables/ipset from working. Also, a lot of these server don’t even keep their list of Geo IP up to date. I prefer the second way. You get the list of Geo IP from a webpage, then you simply copy that list in a text file on your server. The drawback is that you need to keep that list updated by yourself (or you can even create a script with cron that will do this for you). Either way, this tutorial is trying to make it simple and I’ll use the second way. Suck it up! So one of the most popular website for Geo IP databse used to be the ipdeny.com website but it seems that they haven’t updated in a while. Other database offers some restricted free access to the list but most of them will charge you a subscription fee – this usually include the right for making a live query about the Geo IP connecting. You get what you pay for. For a free access, you can get the list of the countries that you want from either ip2location, Maxmind Geolite Free Database or Country IP Blocks. The one that I prefer, it’s free and up to date is the one from Simple Penguin (damn the site now seems to be down, I now recommend ipdeny.com). This list is optimized to be the shortest possible in CIDR format. Click on the country you want to ban and download the flat text file to your linux server. Now, you have ipset and a list of the country you want to ban. The next question you should ask before dropping the hammer of banning, do you want to ban that country from connecting to any ports on your server – like all the ports. Or do you want to ban a country for connecting to a specific port – like port 22 for the sshd service?

Now it's time to use my mighty hammer of banning.

Now it’s time to use my mighty hammer of banning.

Before running the loop for creating the ban, let’s create a set in IPSet, we’ll call it “geoblock”, the name can be whatever you want, this set will be populated with the IP list of the countries you want to ban.

sudo ipset create geoblock hash:net,port

Here’s the loop to run for banning a list of countries from reaching the service of SSHD, if you want to ban another service, simply change the default port of 22 to the one you want, On the first line, between the {} input the country code you wish to ban with a , between each of them – I also recommend that you save this loop in a shell script file :

for IP in $(wget -O – http://www.ipdeny.com/ipblocks/data/countries/{cn,ru,kr,pk,tw,sg,hk}.zone)
do
# regular ban - block port 22 for countryXX
sudo ipset add geoblock $IP,22
done

If you want to use your hammer of banning for ever reaching any of the services on your server, we simply change the loop code for this one instead ;
for IP in $(wget -O – http://www.ipdeny.com/ipblocks/data/countries/{cn,ru,kr,pk,tw,sg,hk}.zone)
do
# ban everything - block countryX
sudo ipset add geoblock $IP
done

Now let’s see if your set has been populated correctly, simply run this command to show the content of the set called geoblock :

# sudo ipset list geoblock

You should get a list of IP addresses in the CIDR format. You can compare it from the original list where you got it to make sure that everything is alright. If you want to delete the complete set, simply use the following command ;

# sudo ipset del geoblock|"setname"

For all the details on ipset, check the manual of the command. Now that the set is created just like you want, we need to add this to the rules of IPtables. You can do it by running this command :

# sudo iptables -I INPUT -m set --set geoblock src -j DROP

If you restart iptables, this rule will not be persistent, don’t forget to run the command of “# service iptables save” in order to make it permanent. There is also another useful trick, it’s to make a reverse list. let’s say that you do not want to block specific countries but rather you would only like to allow only some countries from your list to communicate with your server and ban the rest? It’s rather simple and clever. You need to edit the loop script of creating your IPset, but instead of choosing the countries you want to ban, you will only choose the countries you want to allow and create your list from that. Afterward, you will use this command for iptables to drop all incoming connection from IPs that are not listed in your set of IPSet ;

# sudo iptables -A INPUT -m set --set !geoblock src -j DROP

Again, don’t forget to save your iptables rules if you want to make it permanent. That’s about it, you now have the power to block entire countries from communicating with your servers.

Author: DGhost

System Administrator and consultant for more than 14 years. I've always used computers since I was a kid. I' ve specialized in networking, servers and the inner workings of the Internet. My blog is aimed as a personnel point of view on some technologies, the web, sciences and the Internet in general. If you are wondering why this website is in French and English, that's because I'm a french Canadian who also speaks English and sometimes, when I'm drunk, dabble in Spanish. Consultant et administrateur de système informatisé depuis plus de 14 ans, DGhost est plongé dans l’informatique depuis son plus jeune âge. Spécialiste des réseaux et serveurs, le fonctionnement de l’Internet n’a plus de secret pour lui. Son blog se veut une réflexion sur le web, l'informatique, les sciences et la technologie.

32 Comments

Leave a Reply

Required fields are marked *.



 

This site uses Akismet to reduce spam. Learn how your comment data is processed.