How to Secure NGINX with Custom Fail2ban Filters

Fail2Ban is a great security measure to deploy for your web application server. It comes with a range of features, default filters, and actions that can immediately impact banning bad web bots, draining your system resources, and stopping attacks, which is the most crucial part of any website.

However, most people system admins and website owners are looking for sometimes a bit more extra than what fail2ban has to offer. In the following tutorial, you will learn how to create and use custom filters on your Nginx server, which can be fine-tuned to suit your own needs and expanded later.

Make sure to test and use with caution any new fail2ban filters. Note that settings will need to be adjusted to your needs.

Add New Fail2Ban Jails

The tutorial assumes you have Fail2ban installed and are familiar with how to configure jails and settings. Below are some additional jails you will need to tweak to your liking if you are using further ban actions like Cloudflare or reporting to AbuseIPDB.org etc.

Add the following jails that you wish to use in your /fail2ban/jail.local file.

sudo nano /etc/fail2ban/jail.local
[nginx-403]
 enabled = true
 port     = http,https
 filter = nginx-403
 action = iptables-allports
 logpath = %(nginx_access_log)s
 bantime = 1440m # 1 day
 findtime = 1440m # 1 day
 maxretry = 4

[nginx-noagent]
 enabled = true
 port     = http,https
 filter = nginx-noagent
 action = iptables-allports
 logpath = %(nginx_access_log)s
 bantime = 1440m # 1 day
 findtime = 1440m # 1 day
 maxretry = 3

[nginx-noauth]
 enabled = true
 filter = nginx-noauth
 action = iptables-allports
 logpath = %(nginx_error_log)s
 bantime = 1440m # 1 day
 findtime = 1440m # 1 day
 maxretry = 5

[nginx-nologin]
 enabled = true
 filter = nginx-nologin
 action = iptables-allports
 logpath = %(nginx_access_log)s
 bantime = 1440m # 1 day
 findtime = 1440m # 1 day
 maxretry = 5

[nginx-noscript]
 enabled = true
 action = iptables-allports
 filter = nginx-noscript
 logpath = %(nginx_access_log)s
 bantime = 1440m # 1 day
 findtime = 1440m # 1 day
 maxretry = 3

[nginx-noproxy]
 enabled = true
 action = iptables-allports
 filter = nginx-noproxy
 logpath = %(nginx_access_log)s
 bantime = 1440m # 1 day
 findtime = 1440m # 1 day
 maxretry = 0

[nginx-nowordpress]
 enabled = true
 action = iptables-allports
 filter = nginx-nowordpress
 logpath = %(nginx_access_log)s
 bantime = 1440m # 1 day
 findtime = 1440m # 1 day
 maxretry = 3

[portscan-block]
 enabled = true
 action = iptables-allports
 filter = portscan-block
 logpath = %(ufw_log)s
 bantime = 1440m # 1 day
 findtime = 1440m # 1 day
 maxretry = 5

Advertisement


Create new Fail2Ban Filters

Next, you need to make a new filter file for each of these filters and put .conf at the end of the file.

Note, the create file is something you need to do yourself.

create file /location/fail2ban/filter.d/nginx-403.conf

 [Definition]
 failregex = ^ -."(GET|POST|HEAD).HTTP.*" 403
 ignoreregex =

create file /location/fail2ban/filter.d/nginx-noagent.conf

 [Definition]
 failregex = ^ -."-" "-"$             ^ -."-" "curl.*"$
 ignoreregex =

create file /location/fail2ban/filter.d/nginx-noauth.conf

 [Definition]
 failregex = no user/password was provided for basic authentication.client:              user . was not found in.client:              user . password mismatch.*client: 
 ignoreregex =

create file /location/fail2ban/filter.d/nginx-nologin.conf
 [Definition]
 failregex = ^ -.*POST /sessions HTTP/1.." 200
 ignoreregex =

create file /location/fail2ban/filter.d/nginx-noscript.conf
 [Definition]
 failregex = ^ -.GET.(.php|.asp|.exe|.pl|.cgi|\scgi)
 ignoreregex =

 create file /location/fail2ban/filter.d/nginx-noproxy.conf
 [Definition]
 failregex = ^ -.GET http.
 ignoreregex =

create file /location/fail2ban/filter.d/nginx-nowordpress.conf

 [Definition]
 failregex = ^ .* "(GET|POST|HEAD) /+(?i)(wp(-|/)|xmlrpc.php|\?author=1)
             ^ .* "(GET|POST|HEAD|PROPFIND) /+(?i)(a2billing|admin|apache|axis|blog|cfide|cgi|cms|config|etc|.git|hnap|inc|jenkins|jmx-|joomla|lib|linuxsucks|msd|muieblackcat|mysql|myadmin|n0w|owa-autodiscover|pbxip|pma|recordings|sap|sdk|script|service|shell|sqlite|vmskdl44rededd|vtigercrm|w00tw00t|webdav|websql|wordpress|xampp|xxbb)
             ^ .* "(GET|POST|HEAD) /[^"]+.(asp|cgi|exe|jsp|mvc|pl)( |\?)
             ^ .*(?i)(/bash|burger-imperia|changelog|hundejo|hvd-store|jorgee|masscan|pizza-imperia|pizza-tycoon|servlet|testproxy|uploadify)
 ignoreregex =
 journalmatch = _SYSTEMD_UNIT=nginx.service + _COMM=nginx

create file /location/fail2ban/filter.d/portscan-block.conf

 [Definition]
 failregex = .[UFW BLOCK] IN=. SRC=
 ignore common multicast device discovery calls on LOCAL IPv4/IPv6 networks
 still ban non-local (WAN) calls to any associated ports
 ignoreregex = SRC=(10.|172.1[6-9].|172.2[0-9].|172.3[0-1].|192.168.|fe\w:). DST=(static.ip.address.here|224.0.0.). PROTO=(2|UDP)(\s+|.* DPT=(1900|3702|5353|5355) LEN=\d*\s+)$

Test New Fail2ban Jails/Filters

To finish it off, make sure to restart fail2ban in your operating system via its command.

For most Linux distributions, the following command should work if running systemd.

sudo systemctl restart fail2ban

Other non-systemd distributions or other systems restart your system or fail2ban service.

Once fail2ban has been restarted, run the following fail2ban-client status command to view the jail.

sudo fail2ban-client status nginx-noscripts

Example output:

Status for the jail: nginx-noscripts
 |- Filter
 | |- Currently failed: 0
 | |- Total failed: 0
 | - File list: /var/log/nginx/access.log - Actions
 |- Currently banned: 95
 |- Total banned: 107
 `- Banned IP list:

As the output above has shown, you have 95 IP addresses banned, with a total of 107 including active and inactive historical bans.


Advertisement


Comments and Conclusion

You have learned how to configure some additional jails to your fail2ban configuration for your Nginx server in the tutorial. Overall, you can achieve some excellent server-side security, filters can be adjusted and tweaked, and what was shown is just a scrape of what more can be done to suit specific situations and needs.

The only drawback with fail2ban is running clusters of servers, for now, this isn’t viable unless you use Cloudflare and block them at the reverse proxy, for example, but for single server operators, you can practically lock your server like fort Knox.

Subscribe
Notify of
15 Comments
Inline Feedbacks
View all comments

Hi theгe! Would you mind іf I share your blog with my myspace
group? There’s a lot of folks that I think would really appreciate your
content. Please let me know. Thanks

Hallo,
habe bereits Fail2Ban laufen mit 13 Jails.
Habe jetzt Deinen nginx-nowordpress Jail in meine Jail.local hinzugefügt und auch die nginx-nowordpress erstellt.
Nach service fail2ban restart habe ich aber auf einmal nur noch 12 Jails, eigentlich müssten es aber 14 sein.

2 Jails die nicht in der Jail.local sind, sondern in xy.local und xyz.local sind also weg…

Hi,

I checked the no-wordpress Jail again. Removed “journalmatch = _SYSTEMD_UNIT=nginx.service + _COMM=nginx” from the filter. => After that one of my already existing jails does not work anymore. Disabled your no-wordpress jail => all my jails working again.

Tried your portscan-block jail incl. the filter. => Fail2ban does not restart anymore. Have to disable your jail again.

Tried your no-wordpress jail again, removed “journalmatch = _SYSTEMD_UNIT=nginx.service + _COMM=nginx” from the filter. => one of my already existing jails does not work anymore. Disabled your no-wordpress jail, all my jails working again.

Tried also portscan-block jail => fail2ban does not start anymore. Have to disable your jail again and everything is fine again

Thanks Joshua, I will prepare this the next days

I have sent you a mail with the files:) Many Thanks!

Wow, that was a fast reply, thanks.
I’m on Ubuntu 20.4 LTS with Fail2Ban V 1.57 (the latest you get with apt update).
No, I have 11 of 13 jails in jail.local. Only 2 Jails are in separate jails for whatever reason…

Thanks for checking

Michael

Sorry fail2ban-client -V show me 0.11.1

Yes with Nginx, I will try to change the syntax for the filter as proposed in your first quote…

But for now I have to go to bed:)

Thanks for the fast help so far:)

15
0
Would love your thoughts, please comment.x