Back to Home

Fail2ban integration with CSF to counteract DDoS on nginx / SIM-Networks Blog

linux · ddos · fail2ban · nginx · debian

Fail2ban integration with CSF to counteract DDoS on nginx

  • Tutorial
The ConfigServer Security & Firewall (CSF) script suite initially has a fairly rich set of features for securing the Web hosting server using the iptables packet filter. In particular, it can be used to resist flooding of an attacked host with TCP and SYN, UDP, and ICMP packets of weak and medium strength. CSF is complemented by the built-in Login Failure Daemon (lfd), which monitors the logs for the presence of numerous unsuccessful login attempts in various network services in order to select a password. Such attempts are blocked by adding the IP address of the attacker to the CSF blacklist.

There is another third-party tool that implements similar functionality: Fail2ban. Despite the similarity of the tasks being solved, there is a cardinal difference between lfd and Fail2ban. The first has a closed architecture and supports a limited set of services. While the second allows you to independently develop filters for almost any task. However, CSF and Fail2ban coexist badly on the same server, because they handle iptables rules somewhat unceremoniously. We will try to solve this problem using the Linux Debian v7.XX amd64 OS as an example, so as to maximize the capabilities of both tools. And as an example, we will organize protection against DDoS attacks on nginx.

In my configuration, CSF was originally installed and configured on the server. I will not dwell on this issue in detail; there is enough material on it. I recommend making the following settings in the file "/etc/csf/csf.conf":
SYNFLOOD = "1"
SYNFLOOD_RATE = "100 / s"
SYNFLOOD_BURST = "10"
CONNLIMIT = "80; 110,443; 110"
PORTFLOOD = "80; tcp; 20; 1,443; tcp; 20; 1"
CT_LIMIT = "300"
CT_INTERVAL = "60"


Such a configuration will allow CSF to counteract those hosts that are involved in flood attacks by TCP SYN packets or open a large number of connections to the HTTP server TCP ports.
We proceed to the installation of Fail2ban, after which it must be stopped and autorun disabled:
service fail2ban stop
update-rc.d -f fail2ban remove


We will launch Fail2ban by means of CSF. To do this, create the script "/etc/csf/csfpost.sh":
#! / bin / sh
/etc/init.d/fail2ban reload


It will provide automatic loading of Fail2ban rules in iptables if CSF reloads its rules, for example, in case of updating.
The idea of ​​integrating Fail2ban with CSF is based on the fact that the first will use the blacklist of the second, and not the iptables rules directly, to block the IP addresses of attackers. However, we do not refuse completely iptables in Fail2ban.
Disable all filters in Fail2ban. Most of them are covered by lfd. Fail2ban will be used only for what does not support lfd.
sed -i "s | enabled = true | enabled = false | g" /etc/fail2ban/jail.conf


Add CSF support to Fail2ban. To do this, create the settings file "/etc/fail2ban/action.d/csf-ip-deny.conf" with the following contents:
[Definition]
actionstart =
actionstop =
actioncheck =
actionban = csf -d  Added by Fail2Ban for 
actionunban = csf -dr 


Replace for all Fail2ban filters the blocking action with the created “csf-ip-deny”:
sed -i -e "s | banaction = | banaction = csf-ip-deny \ n # banaction = |" /etc/fail2ban/jail.conf


For particularly annoying intruders, a long lock is provided. This mechanism is implemented by monitoring the journal of Fail2ban itself. Create the settings file "/etc/fail2ban/filter.d/fail2ban.conf":
[Definition]
# Count all bans in the logfile
failregex = fail2ban.actions: WARNING \ [(. *) \] Ban 
# Ignore our own bans, to keep our counts exact.
# In your config, name your jail 'fail2ban', or change this line!
ignoreregex = fail2ban.actions: WARNING \ [fail2ban \] Ban 


Add the following lines to "/etc/fail2ban/jail.conf":
## fail2ban with CSF to block repeat offenders
[fail2ban]
enabled = true
filter = fail2ban
action = iptables-allports
# sendmail-whois [name = fail2ban]
logpath = /var/log/fail2ban.log
maxretry = 10
# Find-time: 1 day
findtime = 86400
# Ban-time: 1 week
bantime = 604800


Those. those of cybercriminals who were blocked by other filters 10 or more times during the day will be blocked by this filter for a week. Note that the action is iptables-allports, not csf-ip-deny. It's not a mistake. So it is necessary that the long blocking does not happen to be accidentally removed by other filters.

Now, by the example of organizing nginx protection against DDoS attacks, consider creating Fail2ban filtering rules. Let's start by configuring nginx, it is necessary to use the capabilities of the ngx_http_limit_conn_module and ngx_http_limit_req_module modules in it . To do this, add the following lines to the settings:
limit_conn_zone $ binary_remote_addr zone = perip: 10m;
limit_conn perip 100;
limit_conn_zone $ server_name zone = perserver: 10m;
limit_conn perserver 200;
limit_req_zone $ binary_remote_addr zone = reqip: 10m rate = 10r / s;
limit_req zone = reqip burst = 30;


Thus, we set limits for no more than 100 connections from one IP address at a time with a speed of 10-30 new connections per second and no more than 200 - to one site from just any number of IP addresses. Excesses will be recorded in the error log, for which we will configure Fail2ban filters.
We create the following settings files.
"/etc/fail2ban/filter.d/nginx-conn-limit.conf"
# Fail2Ban configuration file
#
[Definition]
# Option: failregex
# Notes .: Regexp to catch a generic call from an IP address.
# Values: TEXT
#
failregex = limiting connections by zone. * client: 
# Option: ignoreregex
# Notes .: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =


"/etc/fail2ban/filter.d/nginx-req-limit.conf"
# Fail2Ban configuration file
#
[Definition]
# Option: failregex
# Notes .: Regexp to catch a generic call from an IP address.
# Values: TEXT
#
failregex = limiting requests. * client: 
# Option: ignoreregex
# Notes .: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =


"/etc/fail2ban/filter.d/nginx-dos.conf"
# Fail2Ban configuration file
#
[Definition]
# Option: failregex
# Notes .: Regexp to catch a generic call from an IP address.
# Values: TEXT
#
failregex = ^ -. * "(GET | POST). * HTTP. *" $
# Option: ignoreregex
# Notes .: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =


Add to "/etc/fail2ban/jail.conf":
[nginx-conn-limit]
enabled = true
filter = nginx-conn-limit
action = csf-ip-deny [name = nginx-conn-limit]
logpath = /var/log/nginx/error.log
maxretry = 4
findtime = 21600
bantime = 3600
[nginx-req-limit]
enabled = true
filter = nginx-req-limit
action = csf-ip-deny [name = nginx-req-limit]
logpath = /var/log/nginx/error.log
maxretry = 4
findtime = 21600
bantime = 3600
[nginx-dos]
# Based on apache-badbots but a simple IP check (any IP requesting more than
# 240 pages in 60 seconds, or 4p / s average, is suspicious)
enabled = true
filter = nginx-dos
action = csf-ip-deny [name = nginx-dos]
logpath = /var/log/nginx/access.log
maxretry = 240
findtime = 60
bantime = 3600


Filters "nginx-conn-limit" and "nginx-req-limit" will block those who exceed the limits for connecting to nginx, and "nginx-dos" will block those who access websites too often: more than 240 pages per minute.

Often the goal of attacks is the widespread WordPress CMS: password guessing and a large number of XML-RPC requests . We organize protection from them, too.
"/etc/fail2ban/filter.d/nginx-wp-login.conf"
[Definition]
failregex =  . * POST /wp-login.php
ignoreregex =
"/etc/fail2ban/filter.d/nginx-wp-xmlrpc.conf"
[Definition]
failregex =  . * POST /xmlrpc.php
ignoreregex =


"/etc/fail2ban/filter.d/nginx-wp-register.conf"
[Definition]
failregex = ^ . * "GET /wp-login.php\?action=register HTTP /.*". * $
ignoreregex =


Add to "/etc/fail2ban/jail.conf":
[nginx-wp-login]
enabled = true
filter = nginx-wp-login
action = csf-ip-deny [name = nginx-wp-login]
logpath = /var/log/nginx/access.log
maxretry = 4
findtime = 600
bantime = 3600
[nginx-wp-xmlrpc]
enabled = true
filter = nginx-wp-xmlrpc
action = csf-ip-deny [name = nginx-wp-xmlrpc]
logpath = /var/log/nginx/access.log
maxretry = 4
findtime = 600
bantime = 3600
[nginx-wp-register]
enabled = true
filter = nginx-wp-register
action = csf-ip-deny [name = nginx-wp-register]
logpath = /var/log/nginx/access.log
maxretry = 4
findtime = 600
bantime = 3600


Here's what you can see in the Fail2ban log during a DDoS attack on nginx:
spoiler
2015-01-04 13: 44: 10,660 fail2ban.actions: WARNING [nginx-req-limit] Ban 188.191.47.46
2015-01-04 13: 44: 11,668 fail2ban.actions: WARNING [nginx-conn-limit] Ban 109.187.63.199
2015-01-04 13: 44: 21,061 fail2ban.actions: WARNING [nginx-req-limit] 188.191.47.46 already banned
2015-01-04 13: 44: 29,382 fail2ban.actions: WARNING [nginx-conn-limit] Ban 178.123.155.115
2015-01-04 13: 44: 36,584 fail2ban.actions: WARNING [nginx-conn-limit] Ban 109.62.153.190
2015-01-04 13: 44: 38,246 fail2ban.actions: WARNING [nginx-dos] Ban 5.143.158.88
2015-01-04 13: 44: 38,826 fail2ban.actions: WARNING [nginx-req-limit] Ban 178.158.206.140
2015-01-04 13: 44: 41,739 fail2ban.actions: WARNING [nginx-conn-limit] Ban 5.44.168.38
2015-01-04 13: 44: 49,877 fail2ban.actions: WARNING [nginx-dos] Ban 91.214.131.71
2015-01-04 13: 44: 52,333 fail2ban.actions: WARNING [nginx-conn-limit] Ban 176.125.48.22
2015-01-04 13: 44: 53,395 fail2ban.actions: WARNING [nginx-req-limit] Ban 91.207.211.222
2015-01-04 13: 44: 53,773 fail2ban.actions: WARNING [nginx-dos] Ban 178.158.206.140
2015-01-04 13: 44: 54,849 fail2ban.actions: WARNING [nginx-conn-limit] Ban 5.143.158.88
2015-01-04 13: 44: 57,395 fail2ban.actions: WARNING [nginx-req-limit] 91.207.211.222 already banned
2015-01-04 13: 44: 57,765 fail2ban.actions: WARNING [nginx-dos] Ban 37.232.87.169
2015-01-04 13: 44: 58,073 fail2ban.actions: WARNING [nginx-conn-limit] Ban 77.34.22.95
2015-01-04 13: 44: 58,506 fail2ban.actions: WARNING [nginx-req-limit] Ban 93.80.45.244
2015-01-04 13: 45: 02,733 fail2ban.actions: WARNING [nginx-dos] Ban 176.120.38.238
2015-01-04 13: 45: 05,615 fail2ban.actions: WARNING [nginx-conn-limit] Ban 178.173.4.162


And this is how overly annoying intruders will be blocked:
spoiler
2015-01-04 11: 43: 29,618 fail2ban.actions: WARNING [nginx-wp-login] Ban 95.163.121.129
2015-01-04 12: 43: 30,160 fail2ban.actions: WARNING [nginx-wp-login] Unban 95.163.121.129
2015-01-04 12: 56: 53,543 fail2ban.actions: WARNING [nginx-wp-login] Ban 95.163.121.129
2015-01-04 13: 56: 54,279 fail2ban.actions: WARNING [nginx-wp-login] Unban 95.163.121.129
2015-01-04 14: 02: 40,932 fail2ban.actions: WARNING [nginx-wp-login] Ban 95.163.121.129
2015-01-04 15: 02: 41,040 fail2ban.actions: WARNING [nginx-wp-login] Unban 95.163.121.129
2015-01-04 15: 12: 16,906 fail2ban.actions: WARNING [nginx-wp-login] Ban 95.163.121.129
2015-01-04 16: 12: 16,937 fail2ban.actions: WARNING [nginx-wp-login] Unban 95.163.121.129
2015-01-04 16: 55: 33,362 fail2ban.actions: WARNING [nginx-wp-login] Ban 95.163.121.129
2015-01-04 17: 55: 34,142 fail2ban.actions: WARNING [nginx-wp-login] Unban 95.163.121.129
2015-01-04 18: 23: 37,665 fail2ban.actions: WARNING [nginx-wp-login] Ban 95.163.121.129
2015-01-04 19: 23: 38,136 fail2ban.actions: WARNING [nginx-wp-login] Unban 95.163.121.129
2015-01-04 19: 45: 46,850 fail2ban.actions: WARNING [nginx-wp-login] Ban 95.163.121.129
2015-01-04 20: 03: 17,247 fail2ban.actions: WARNING [nginx-wp-login] Ban 92.255.28.42
2015-01-04 20: 45: 47.085 fail2ban.actions: WARNING [nginx-wp-login] Unban 95.163.121.129
2015-01-04 21: 03: 17,297 fail2ban.actions: WARNING [nginx-wp-login] Unban 92.255.28.42
2015-01-04 21: 09: 51,996 fail2ban.actions: WARNING [nginx-wp-login] Ban 92.255.28.42
2015-01-04 21: 16: 22,336 fail2ban.actions: WARNING [nginx-wp-login] Ban 95.163.121.129
2015-01-04 22: 09: 52,036 fail2ban.actions: WARNING [nginx-wp-login] Unban 92.255.28.42
2015-01-04 22: 13: 27,799 fail2ban.actions: WARNING [nginx-wp-login] Ban 92.255.28.42
2015-01-04 22: 16: 23,295 fail2ban.actions: WARNING [nginx-wp-login] Unban 95.163.121.129
2015-01-04 22: 24: 56,755 fail2ban.actions: WARNING [nginx-wp-login] Ban 95.163.121.129
2015-01-04 23: 13: 28,058 fail2ban.actions: WARNING [nginx-wp-login] Unban 92.255.28.42
2015-01-04 23: 16: 50,235 fail2ban.actions: WARNING [nginx-wp-login] Ban 92.255.28.42
2015-01-04 23: 24: 56,843 fail2ban.actions: WARNING [nginx-wp-login] Unban 95.163.121.129
2015-01-05 00: 00: 42,183 fail2ban.actions: WARNING [nginx-wp-login] Ban 95.163.121.129
2015-01-05 00: 00: 43,851 fail2ban.actions: WARNING [fail2ban] Ban 95.163.121.129
2015-01-05 00: 16: 50,263 fail2ban.actions: WARNING [nginx-wp-login] Unban 92.255.28.42
2015-01-05 00: 23: 22,863 fail2ban.actions: WARNING [nginx-wp-login] Ban 92.255.28.42
2015-01-05 01: 00: 42,637 fail2ban.actions: WARNING [nginx-wp-login] Unban 95.163.121.129
2015-01-05 01:23:23,750 fail2ban.actions: WARNING [nginx-wp-login] Unban 92.255.28.42
2015-01-05 01: 26: 16,543 fail2ban.actions: WARNING [nginx-wp-login] Ban 92.255.28.42
2015-01-05 02:26: 16,681 fail2ban.actions: WARNING [nginx-wp-login] Unban 92.255.28.42
2015-01-05 02: 32: 28,850 fail2ban.actions: WARNING [nginx-wp-login] Ban 92.255.28.42
2015-01-05 03: 32: 29,350 fail2ban.actions: WARNING [nginx-wp-login] Unban 92.255.28.42
2015-01-05 03: 39: 18,048 fail2ban.actions: WARNING [nginx-wp-login] Ban 92.255.28.42
2015-01-05 04: 39: 18,609 fail2ban.actions: WARNING [nginx-wp-login] Unban 92.255.28.42
2015-01-05 04: 43: 38,428 fail2ban.actions: WARNING [nginx-wp-login] Ban 92.255.28.42
2015-01-05 05:43: 39,091 fail2ban.actions: WARNING [nginx-wp-login] Unban 92.255.28.42
2015-01-05 05: 47: 47,722 fail2ban.actions: WARNING [nginx-wp-login] Ban 92.255.28.42
2015-01-05 05: 47: 50,212 fail2ban.actions: WARNING [fail2ban] Ban 92.255.28.42
2015-01-05 06: 47: 48,343 fail2ban.actions: WARNING [nginx-wp-login] Unban 92.255.28.42


Recently, cases have become more frequent when attackers carry out distributed enumeration of passwords from different IP addresses of one class C subnet. The Fail2ban operation algorithm is not able to recognize this behavior:
spoiler
2015-01-05 14: 01: 14,432 fail2ban.actions: WARNING [nginx-wp-login] Ban 193.176.147.176
2015-01-05 14: 01: 14,656 fail2ban.actions: WARNING [nginx-wp-login] Unban 193.176.147.249
2015-01-05 14: 01: 35,906 fail2ban.actions: WARNING [nginx-wp-login] Ban 193.176.147.198
2015-01-05 14: 02: 39,536 fail2ban.actions: WARNING [nginx-wp-login] Unban 193.176.147.154
2015-01-05 14: 02: 53,766 fail2ban.actions: WARNING [nginx-wp-login] Ban 193.176.147.132
2015-01-05 14: 02: 53,980 fail2ban.actions: WARNING [nginx-wp-login] Unban 193.176.147.202
2015-01-05 14: 04: 00,782 fail2ban.actions: WARNING [nginx-wp-login] Ban 193.176.147.136
2015-01-05 14: 04: 05,007 fail2ban.actions: WARNING [nginx-wp-login] Unban 193.176.147.104
2015-01-05 14: 04: 07,234 fail2ban.actions: WARNING [nginx-wp-login] Ban 193.176.147.167
2015-01-05 14: 04: 25,473 fail2ban.actions: WARNING [nginx-wp-login] Ban 193.176.147.143
2015-01-05 14: 05: 26,993 fail2ban.actions: WARNING [nginx-wp-login] Unban 193.176.147.253
2015-01-05 14: 06: 25,719 fail2ban.actions: WARNING [nginx-wp-login] Unban 193.176.147.230
2015-01-05 14: 06: 27,945 fail2ban.actions: WARNING [nginx-wp-login] Ban 193.176.147.162
2015-01-05 14: 07: 23,484 fail2ban.actions: WARNING [nginx-wp-login] Ban 193.176.147.153
2015-01-05 14: 07: 56,962 fail2ban.actions: WARNING [nginx-wp-login] Ban 193.176.147.140
2015-01-05 14: 08: 11,207 fail2ban.actions: WARNING [nginx-wp-login] Unban 193.176.147.142
2015-01-05 14: 09: 37,759 fail2ban.actions: WARNING [nginx-wp-login] Unban 193.176.147.137
2015-01-05 14: 10: 59,757 fail2ban.actions: WARNING [nginx-wp-login] Unban 193.176.147.175
2015-01-05 14: 11: 04,030 fail2ban.actions: WARNING [nginx-wp-login] Unban 193.176.147.117
2015-01-05 14: 11: 23,273 fail2ban.actions: WARNING [nginx-wp-login] Ban 193.176.147.218
2015-01-05 14: 11: 41,517 fail2ban.actions: WARNING [nginx-wp-login] Unban 193.176.147.139
2015-01-05 14: 13: 22,590 fail2ban.actions: WARNING [nginx-wp-login] Ban 193.176.147.204
2015-01-05 14: 13: 24,808 fail2ban.actions: WARNING [nginx-wp-login] Ban 193.176.147.220
2015-01-05 14: 14: 36,124 fail2ban.actions: WARNING [nginx-wp-login] Ban 193.176.147.190
2015-01-05 14: 14: 38,356 fail2ban.actions: WARNING [nginx-wp-login] Ban 193.176.147.179
2015-01-05 14: 14: 38,577 fail2ban.actions: WARNING [nginx-wp-login] Ban 193.176.147.137
2015-01-05 14: 14: 49,805 fail2ban.actions: WARNING [nginx-wp-login] Unban 193.176.147.188
2015-01-05 14: 15: 48,398 fail2ban.actions: WARNING [nginx-wp-login] Unban 193.176.147.131


Eliminate this shortcoming with the following script, which will be executed every hour by the cron scheduler.
"/etc/cron.hourly/fail2ban-subnets"
#! / bin / bash
log = "/ var / log / fail2ban.log"
limit = 30
grep = `which grep`
$ {grep} "fail2ban.actions. * Ban" $ {log} | $ {grep} -E -o "(25 [0-5] | 2 [0-4] [0-9] | [01]? [0-9] [0-9]?) \. (25 [ 0-5] | 2 [0-4] [0-9] | [01]? [0-9] [0-9]?) \. (25 [0-5] | 2 [0-4] [ 0-9] | [01]? [0-9] [0-9]?) \. (25 [0-5] | 2 [0-4] [0-9] | [01]? [0- 9] [0-9]?) "| awk -F '.' '{print $ 1 "." $ 2 "." $ 3}' | sort -u | while read line
	do
		count = $ ($ {grep} -c "fail2ban.actions. * Ban. * $ {line}" $ {log})
			if [$ {count} -ge $ {limit}]
				then
					/ usr / sbin / csf -td $ {line} .0 / 24 7d "Subnet $ {line} .0 / 24 is blocked for a week by Fail2ban after $ {count} attempts"
			fi
	done
exit 0


Those. Class C IP networks will be completely blocked for a week, if earlier other Fail2ban filters have been triggered to addresses from them 30 or more times.
Here is the result:
# csf -t
A / D IP address Port Dir Time To Live Comment
DENY 193.176.147.0/24 * in 6d 21h 34m 18s Subnet 193.176.147.0/24 is blocked for a week by Fail2ban after 641 attempts
DENY 46.148.30.0/24 * in 6d 21h 34m 19s Subnet 46.148.30.0/24 is blocked for a week by Fail2ban after 332 attempts
DENY 46.148.31.0/24 * in 6d 21h 34m 19s Subnet 46.148.31.0/24 is blocked for a week by Fail2ban after 334 attempts


While writing this article, we used the experience from the following resources:
https://extremeshok.com/5030/ubuntu-lts-fail2ban-with-csf-and-blocking-of-repeat-offenders-scan-log-files-to-ban -malicious-ips-and-prevent-brute-forcing-of-logins-with-configs /
http://tecadmin.net/add-custom-iptables-rules-with-csf/
https://rtcamp.com/tutorials / nginx / block-wp-login-php-bruteforce-attack /
https://rtcamp.com/tutorials/nginx/fail2ban/
https://www.xaker.name/forvb/showthread.php?t=28659
https: //beeznest.wordpress.com/2012/06/08/anti-nginx-dos-filter-for-fail2ban-4/
https://debian.pro/1223
http://www.michelem.org/2014/03 / 02 / stopblock-apachenginx-hack-attempts-with-fail2ban /
http://www.krazyworks.com/permanently-ban-ip-with-fail2ban/
http://www.shellhacks.com/en/RegEx-Nayti-IP-Adresa-v-Fayle-s-Pomoshchyu-Grep

Read Next