← Start of tutorial

WIUNIX ACADEMY

Fail2ban for Nginx: trustworthy logs, scoped bans and recovery

Page 5 of 6. Review the earlier steps and prerequisites before continuing.

YOUR SYSTEM. YOUR STEPS.

Where are you working?

Choose your OS and version. The prerequisites, commands and troubleshooting follow your environment.

Ubuntu 26.04 LTSReviewed against official documentation ·

USE YOUR OWN VALUES

Configure this tutorial

Set the example values for your environment; related explanations and copy panels update together. Never enter passwords, private keys or tokens. Values stay in this tab and are not sent to the server or share links. Check actual DNS, certificates and network policy before running commands.

Different IP addresses, domains or ports? Open the settings below, change the values and select “Apply to tutorial”, then copy the updated commands.

Set values for my environment11 editable values
15

LET’S BUILD

Unban and stop only the isolated daemon

Explicit socket selection is mandatory; never omit it in this lab. Confirm the UNBAN record, then stop the scratch instance. Its configuration/evidence can remain in the private directory for review. Do not delete unknown PID/socket files or kill a process merely because its name resembles Fail2ban.

Bash

Dedicated lab host; Bash shell

set -e
sudo fail2ban-client -c "/srv/academy-fail2ban-lab/conf" -s "/srv/academy-fail2ban-lab/fail2ban.sock" set academy-nginx-lab unbanip 192.0.2.60
sudo cat "/srv/academy-fail2ban-lab/actions.log"
sudo fail2ban-client -c "/srv/academy-fail2ban-lab/conf" -s "/srv/academy-fail2ban-lab/fail2ban.sock" stop
How do I know it worked?

UNBAN 192.0.2.60 is recorded; the scratch socket closes; the system service remains masked.

Choose the actual enforcement plane

The following optional activation changes the dedicated lab firewall. nftables[type=multiport] uses an inet table, separate IPv4/IPv6 address sets and an input hook; it rejects TCP 443 only. Existing rules can still block allowed clients. Fail2ban is not a replacement default-deny firewall, and a successful action does not test reachability. Do not switch between iptables-legacy, iptables-nft, native nftables, UFW and firewalld by name alone. Inspect actual hooks, priorities and manager ownership first.

UFW status is not a complete inventory of separately installed nftables rules. A UFW/firewalld reload or ruleset flush can remove another manager’s rules. Use one reviewed ownership model and revalidate after reload/reboot. Docker’s iptables DOCKER-USER guidance does not directly translate to the nftables backend; do not copy this host-input action to forwarded containers.

Open a new local override

On the fresh lab only, disable the package SSH jail explicitly before removing the install-time mask. This guide does not configure or weaken an existing SSH jail. Keep distro jail.conf/filter/action files unchanged. Local files are read after package conf files; later alphabetical local files can override earlier values, so inspect the resolved dump.

Bash

Dedicated lab host; Bash shell

sudo test ! -e /etc/fail2ban/jail.d/90-academy-nginx.local && sudo nano /etc/fail2ban/jail.d/90-academy-nginx.local
16

LET’S BUILD

Configure one port-scoped host jail

Use only after the direct-IP and management checks. The two stanzas below are for the new lab deployment. The IP allowlist does not open a firewall port; it only exempts detection. If you administer over IPv6, add the exact stable IPv6 source here manually before starting. Never use a broad proxy/CDN range as an exemption to pretend client enforcement works.

INI · /etc/fail2ban/jail.d/90-academy-nginx.local

File contents; edit on the dedicated lab host

[sshd]
enabled = false

[academy-nginx]
enabled = true
filter = academy-nginx-401
backend = polling
logpath = /var/log/nginx/academy-auth.log
usedns = no
ignoreip = 127.0.0.1/8 ::1 198.51.100.10
findtime = 600
maxretry = 5
bantime = 600
port = 443
protocol = tcp
banaction = nftables[type=multiport]
action = %(action_)s
How do I know it worked?

Resolved jail targets the correct log/TCP port; package SSH jail remains disabled for this fresh lab.

17

LET’S BUILD

Review the resolved configuration

Do not continue to activation unless only academy-nginx is enabled, the audit rehearsal has passed and the resolved TCP action/allowlist matches your direct host. These commands do not start a jail.

Bash

Dedicated lab host; Bash shell

sudo fail2ban-client -t && sudo fail2ban-client -d
How do I know it worked?

Only the reviewed jail and nftables TCP action appear.

18

LET’S BUILD

Start only the reviewed jail

Read the dump before running the second group: it must show only academy-nginx enabled, no mail/cloud API action, correct allowlist and only the selected TCP port. If other jails appear, stop and reconcile locally. On the authorized dedicated lab, unmask only the mask created earlier, then start. Do not inject fixture lines into the live file or manually ban any real client to demonstrate success. Rules/sets may appear only when the first ban occurs because actions start on demand; absence beforehand is not proof of failure.

Bash

Dedicated lab host; Bash shell

set -e
sudo systemctl unmask fail2ban.service
sudo systemctl enable --now fail2ban.service
sudo fail2ban-client status
sudo fail2ban-client status academy-nginx
sudo fail2ban-client get academy-nginx ignoreip
sudo nft list ruleset
How do I know it worked?

Only the intended jail is active; no deliberate real-address ban was performed; second SSH session still works.

Explore all tutorials ↗