← Start of tutorial

WIUNIX ACADEMY

Fail2ban for Nginx: trustworthy logs, scoped bans and recovery

Page 2 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.10Reviewed 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
03

LET’S BUILD

Install without starting unreviewed jails

Fresh-host precondition: Fail2ban is not installed and no fail2ban.service mask exists. Masking before installation prevents maintainer scripts from starting a package-enabled SSH jail. This mask belongs to this lab procedure and will be removed only after configuration review. Do not mask an existing security service. The commands do not start or enable nftables.service or load a firewall ruleset. Ubuntu may require its official Universe component; if no candidate exists, enable that component through the documented OS repository procedure, not a foreign release.

Bash

Dedicated lab host; Bash shell

set -e
sudo systemctl mask fail2ban.service
sudo apt-get update
apt-cache policy fail2ban nginx nftables
sudo apt-get install --no-install-recommends fail2ban nftables python3 nano
fail2ban-client --version
nginx -v
dpkg-query -W fail2ban nginx nftables
sudo systemctl is-enabled fail2ban.service || true
How do I know it worked?

Correct signed repository packages, actual versions recorded and Fail2ban remains masked/inactive.

04

LET’S BUILD

Preserve the Nginx before-state

Create the new private lab directory only if absent. Store a private Nginx configuration snapshot before editing; it may contain credential references or secrets. Do not upload it. The scratch directory is not an off-host backup. Keep a note of the exact virtual-host file and location you will change; recovery restores only those affected files, not the whole snapshot over concurrent edits.

Bash

Dedicated lab host; Bash shell

set -e
test ! -e "/srv/academy-fail2ban-lab"
test ! -L "/srv/academy-fail2ban-lab"
sudo install -d -m 0700 "/srv/academy-fail2ban-lab"
sudo tar -C /etc -cpf "/srv/academy-fail2ban-lab/nginx-before.tar" nginx
sudo chmod 0600 "/srv/academy-fail2ban-lab/nginx-before.tar"
sudo tar -tf "/srv/academy-fail2ban-lab/nginx-before.tar"
How do I know it worked?

Private snapshot exists and lists the intended Nginx configuration.

Open a new HTTP-context format file

The distribution Nginx configuration normally includes conf.d files inside http. Confirm that in the preflight dump. The new format contains only server-generated time, socket address and numeric status; no URI, username, User-Agent or forwarded header is available for an attacker to inject a fake host. Do not use escape=none. The format name is intentionally fixed, not an application endpoint.

Bash

Dedicated lab host; Bash shell

sudo test ! -e /etc/nginx/conf.d/academy-fail2ban-format.conf && sudo nano /etc/nginx/conf.d/academy-fail2ban-format.conf
05

LET’S BUILD

Define one minimal log format

Save this complete file at HTTP context. $time_iso8601 includes a numeric offset; the filter below consumes exactly that date format. Keep the f2b marker and field order unchanged unless you also revise and retest the filter and fixtures.

Nginx · /etc/nginx/conf.d/academy-fail2ban-format.conf

File contents; edit on the dedicated lab host

log_format academy_auth '$time_iso8601 f2b $remote_addr $status';
How do I know it worked?

One format definition is loaded in http, outside server/location blocks.

06

LET’S BUILD

Attach the log to the existing protected location

Open the exact existing virtual-host file identified by preflight in your protected editor. Add only the access_log line below inside its already working Basic-auth location; retain its auth_basic/auth_basic_user_file, TLS, content handler, routing and other access logs. This is a one-line fragment, not a replacement virtual host. Adding an access_log at a location overrides inherited access logs: explicitly retain any inherited audit log you still require at that location.

Do not log an entire API whose ordinary challenges are 401. Select the real authentication boundary, review location precedence/internal redirects and confirm missing credentials give 401 there. This tutorial neither creates a password file nor collects its secret in browser settings. Keep password/hash files in your existing protected editor and access-control process.

Nginx · Existing protected location — one-line fragment

File contents; edit on the dedicated lab host

access_log /var/log/nginx/academy-auth.log academy_auth;
How do I know it worked?

Only the intended existing authentication location emits this additional minimal log.

07

LET’S BUILD

Validate and reload Nginx

This reload belongs only on your dedicated lab after its before-state is saved. A failed syntax test must prevent reload. Do not change ownership of the whole log directory or make logs world-writable. The Nginx master should create this standard-directory log; inspect its actual owner/mode. Existing logrotate rules must include the chosen .log path and reopen Nginx logs after rotation.

Bash

Dedicated lab host; Bash shell

set -e
sudo nginx -t && sudo systemctl reload nginx
sudo stat -c "%U:%G %a %n" "/var/log/nginx/academy-auth.log"
sudo cat /etc/logrotate.d/nginx
How do I know it worked?

Syntax succeeds, the existing site remains available and the dedicated log is writable only by intended service/root identities.

Explore all tutorials ↗