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.
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
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.
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
Correct signed repository packages, actual versions recorded and Fail2ban remains masked/inactive.
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.
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"
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.
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
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.
File contents; edit on the dedicated lab host
log_format academy_auth '$time_iso8601 f2b $remote_addr $status';
One format definition is loaded in http, outside server/location blocks.
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.
File contents; edit on the dedicated lab host
access_log /var/log/nginx/academy-auth.log academy_auth;
Only the intended existing authentication location emits this additional minimal log.
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.
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
Syntax succeeds, the existing site remains available and the dedicated log is writable only by intended service/root identities.