Aren't you tired of those bot spammers regularly attacking your website? They are numerous of them, literally coming from everywhere spamming your website 24 hours non-stop until your server crashes. Anyway, today I am going to share how you can block them efficiently using iptables.

Scenario: specific IP abuse

Considering this scenario, you are hosting a Wordpress Site on your server running the very standard LAMP stack (Linux + Apache + MySQL + PHP). Suddenly, your site stops responding and the customers starts complaining. As an IT expert, you starts checking the log file and found this:

... 

198.37.97.40 - - [07/Jan/2021:17:34:55 +0000] "POST /wp-login.php HTTP/2.0" 200 7366 "https://www.example.com/wp-login.php" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66" "-"
198.37.97.40 - - [07/Jan/2021:17:34:55 +0000] "POST /wp-login.php HTTP/2.0" 200 7366 "https://www.example.com/wp-login.php" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66" "-"
198.37.97.40 - - [07/Jan/2021:17:34:55 +0000] "POST /wp-login.php HTTP/2.0" 200 7366 "https://www.example.com/wp-login.php" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66" "-"

...

Solution: Block by IP address

The simplest and most effective way is to block them by IP address:

# iptables -I INPUT -p tcp -s 198.37.97.40 -j DROP

All the incoming packets (-I INPUT) coming from the IP address (-s 198.37.97.40) are dropped (-j DROP) for all tcp ports (-p tcp).

Tips | You can run the command line as root user by typing "sudo" at the beginning of your command line. You can also run all the command lines as root user by typing "sudo bash" (depends on your shell).

If you like, you can specify the specific port that you would like to block with --dport(ex: port 80,443):

# iptables -I INPUT -p tcp -s 198.37.97.40 --dport 80,443 -j DROP

If in a later time, you want to remove this rule, you can replace -I with -D:

# iptables -D INPUT -p tcp -s 198.37.97.40 -j DROP

You can also remove the rules by line number. To find out the line numbers, you can type iptables -L INPUT --line-numbers and you will see something like this:

num  target     prot opt source               destination
1    DROP       tcp  --  97.37.198-40.dc74.net  anywhere

Get the line number and delete the rule:

iptables -D INPUT 1

You can also list all the rules you defined by typing iptables -L -n -v. Please visit the official iptables for more information.

Scenario: restrict IP by geolocation

You are doing the business in United States but somehow you are getting the traffics from some other countries. You checked the logs and found out that none of them are legit visitors. Let's say the attackers are mostly coming from France.

Solution: Block by IP geolocation

Blocking IP by its geolocation is done using ipset. Ipset can be used to define a range of IP address and iptables can then be defined whether to block or allow these IPs. The idea is to find the IP ranges that are allocated to the country and block the traffic coming from that country. This solution below is inspired by this article.

Here's the steps:

1. Download the list of IP address by country.

Visit this website, select the Country (ex: France) and set the Output Format as CIDR and click Download button.

download-ip-list-by-country

2. Create a new script file.

Create a script file and copy the IP list from step 1.

# vi block-france.sh

3. Execute the following sed commands.

Modify the file using sed as below:

# sed -i '/^#/d' block-france.sh
# sed -i 's/^/ipset add block-france /g' block-france.sh
# sed  -i '1i ipset create block-france nethash' block-france.sh

If you are doing everything correctly, you will see something like this:

ipset create block-france nethash
ipset add block-france 217.77.240.53/32
ipset add block-france 217.77.240.77/32
ipset add block-france 217.77.240.78/32
ipset add block-france 217.77.240.87/32
ipset add block-france 217.77.240.152/32

...

4. Set the file as executable program.

# chmod +x block-france.sh

5. Execute the program.

# bash block-france.sh

6. Create a new rule in iptables.

# iptables -I INPUT -m set --match-set block-france src -j DROP

That's it! You can test it out by using some sort of VPN software to verify. Likewise, you can repeat the same process for different countries.

Please be noted that these rules you created will not be there after a system reboot, so what you can do is to save the current configuration to a file and restore it on reboot.

Here's how you can save the configuration to a file:

# ipset save > /etc/countryblocker/conf.ipset
# iptables-save > /etc/countryblocker/conf.iptables

Next, you can modify this file(/etc/rc.local) and add these two lines:

ipset restore < /etc/countryblocker/conf.ipset
iptables-restore < /etc/iptables/rules/conf.iptables

You don't need to worry whether these command lines will be executed or not. When you are rebooting the server, you are essentially running as a root user so there won't be any permission-related problem occurred during the reboot.

BONUS

Well, you can choose to block all the traffics and whitelist only the countries that you want. In step 6, change the DROP to ACCEPT, and DROP everything else.

# iptables -I INPUT -m set --match-set accept-france src -j ACCEPT
# iptables -I INPUT -m set --match-set accept-us src -j ACCEPT
# iptables -I INPUT -m set --match-set accept-england src -j ACCEPT
# iptables -A INPUT -p tcp --dport 80,443 -j DROP

Scenario: restrict IP by rate

Your website constantly receives IP attacks from everywhere else. Blocking by IP geolocation does not help either because those attacks are also coming from your own country as well. However, you realize that these attacks all have similar patterns, for example, these attacks focus on the wp-login.php and xmlrpc.php an awful lot.

Solution: Block by IP request rate

As for the solution, we are going to use fail2ban service to block these IP addresses.

Here's all the steps:

1. Install fail2ban (if needed).

It is usually come with the Linux distribution like iptables, but if your server does not have fail2ban, you can try downloading and installing it by sudo yum install fail2ban. If that didn't work, you can try this: sudo yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm. If this doesn't work either, please do some googling yourself.

2. Create new filter file.

You can create a new filter file at /etc/fail2ban/filter.d/wp.conf. You can define failregex based on your log format. For example, this is how my log format looks like:

198.37.97.40 - - [07/Jan/2021:17:34:55 +0000] "POST /wp-login.php HTTP/2.0" 200 7366 "https://www.example.com/wp-login.php" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36 Edg/87.0.664.66" "-"

To match the regex, <HOST> is the IP address, with POST keyword and either wp-login.php or xmlrpc.php and match with the status 200 lastly. Here's the definition look like:

[Definition]
failregex = <HOST>.*POST.*(wp-login\.php|xmlrpc\.php).* 200
ignoreregex =

If your log is in json format, you can also do something similar. For example, here's my log format:

{ "time":"[10/Jan/2021:05:22:55 +0000]", "remoteIP":"192.99.38.64", "host":"www.example.com", "request":"/wp-login.php", "method":"GET", "status":"200", "userAgent":"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.99 Safari/533.4"}

And here's the failregex in the definition:

[Definition]
failregex = ^{.*remoteIP":"<HOST>".*(wp-login\.php|xmlrpc\.php).*POST.*200
ignoreregex =

3. Create new jail file.

You can create a new jail file at /etc/fail2ban/jail.d/wp.conf. Here's what you can write for the jail file:

[wp]
# set to true to allow jail
enabled = true

# ports to be banned
port = 80,443

# match the filter name "wp"
filter = wp

# use the action name "iptables-multiport" and pass in variables as needed
banaction = iptables-multiport[blocktype=DROP,name=wp, port="80,443", protocol=tcp]

# define the path to the log file
logpath = /var/log/httpd/access_log

# set maximum retry count
maxretry = 5

# if "maxretry" is reached within "findtime" (ex: 120 seconds), then the IP will be banned
findtime = 120

# define how long would you like to ban in seconds
bantime = 1800

4. Restart/Reload fail2ban to take effect.

# service fail2ban restart

That's it!

To check for status, you can type fail2ban-client status:

# fail2ban-client status
Status
|- Number of jail:      1
`- Jail list:   wp

To find out the details of the specific jail (ex: wp we defined earlier):

# fail2ban-client status wp
Status for the jail: wp
|- Filter
|  |- Currently failed: 1
|  |- Total failed:     71
|  `- File list:        /var/log/httpd/access_log
`- Actions
   |- Currently banned: 70
   |- Total banned:     659
   `- Banned IP list:   123.123.123.123 xxx.xxx.xxx.xxx ...

BONUS

The default 1800 seconds (30 mins) bantime is not enough because they just keep coming. What you can do is to create another stricter jail to block frequent violators.

Here you can repeat step 3. above, and give it a different name for the jail (ex: wp-adv):

[wp-adv]
# set to true to allow jail
enabled = true

# ports to be banned
port = 80,443

# match the filter name "wp"
filter = wp

# use the action name "iptables-multiport" and pass in variables as needed
banaction = iptables-multiport[blocktype=DROP,name=wp-adv, port="80,443", protocol=tcp]

# define the path to the log file
logpath = /var/log/httpd/access_log

# set maximum retry count (NOTE: this is 3 times more than the regular)
maxretry = 15

# the violators are blocked 3 times in a day and we can consider it is a bad IP
findtime = 86400

# block them indefinitely
bantime = -1

Now, these two rules are running altogether. When the violator violates 5 times, the jail wp will block its further request for 30 mins. The jail wp-adv now accumulates 5 times.

After the first block is removed and if the violator violates another 5 times, a second block will be issued by jail wp for another 30 mins. The jail wp-adv accumulates another 5 times, making it 10 times now.

After the second block is removed and if the violator violates another 5 times, a third block will be issued by wp for 30 mins. This time, the jail wp-adv has already accumulated 15 times and a lifetime ban will be issued. The bad IP can say bye bye forever.

Do not forget to reload the fail2ban service to see the effect!

Another BONUS

If you happen to block an unintended IP from your jail rules, you can unblock the IP with this command line:

fail2ban-client set your_jail_name unbanip your_ip

Conclusion

Iptables is such a powerful tool to manage the inbound/outbound connections. Plus, it is usually come with the Linux distribution and the configuration is not complicated at all. There are lots of things that you can do with iptables, for example, you can block unauthenticated ssh connection, prevent bots from scanning your website, etc. Iptables is very powerful but you must use it with extra care. If you accidentally lock yourself out, you will not be able to get in any more.

That's all about it. Thanks for reading!

Post was published on , last updated on .

Like the content? Support the author by paypal.me!