OneLinersCommand workbench
Guides
Security / Services & Applications

Protect SSH and web authentication with Fail2ban

Use local jail overrides, verify filters against real logs, test bans safely, and retain an unban path.

35 min8 stepsChanges system stateRevision 2
Save or explore
Save to collectionCreate a collection in the sidebar first.
0 of 8 steps completed
Goal

Reduce repeated authentication abuse without locking administrators out.

Supported environments
  • Ubuntu Server 22.04 LTS, 24.04 LTS
  • Fail2ban 1.x
Prerequisites
  • Correct logs Confirm failures appear in journald or selected files.
  • Trusted sources Inventory management and monitoring addresses.
  • Recovery Keep console or a second source available.
Operating boundary

OneLiners never runs these steps or stores secrets. Review placeholders, versions, current state, and change-control requirements before using a command.

Full guide

What you will build

System
  • A distribution-managed Fail2ban installation whose local jail overrides survive package updates and separate journald-backed SSH from file-backed web authentication logs.
  • Measured SSH and Nginx filters, explicit trusted-address exceptions, conservative retry windows, and firewall actions validated before activation.
  • A controlled ban and unban drill from a disposable source, with console recovery, false-positive observation, and rollback.
Observable outcome
  • fail2ban-regex demonstrates that real failure lines match while successful logins and unrelated requests do not.
  • fail2ban-client --test accepts the merged configuration and only the intended sshd and nginx-http-auth jails load.
  • A disposable test address is banned after the measured threshold, current administration remains reachable, and explicit unban removes the address.
  • Operators can identify backend, filter, action, chain, evidence, owner, allowlist, duration, and emergency recovery for every enabled jail.

Architecture

How the parts fit together

Fail2ban reads authentication events through jail-specific backends, applies a filter to extract source addresses, counts failures inside findtime, and invokes a ban action after maxretry. The action inserts temporary firewall state for bantime. ignoreip excludes trusted sources, while fail2ban-client provides status and explicit recovery control.

JailCombines backend, filter, retry window, ban duration, and action for one protected service.
BackendReads either the systemd journal for sshd or Nginx log files for web authentication.
FilterMatches known failure lines and extracts a trustworthy source address.
DatabaseRetains Fail2ban state across restarts according to local configuration.
Firewall actionAdds and removes temporary source-address blocks.
Recovery pathProvides console or independent administration plus explicit unban and jail disablement.
  1. sshd writes authentication failure events to journald; Nginx writes HTTP authentication failures to its error log.
  2. Each jail backend feeds events to its selected filter.
  3. Fail2ban counts matches for each source during findtime and triggers the configured action after maxretry.
  4. The firewall blocks that source for bantime while status and journal record the decision.
  5. Expiry or an explicit unban removes action state; operators review false positives and tune from evidence.

Assumptions

  • The host already uses a reviewed firewall framework compatible with the selected Fail2ban banaction.
  • SSH client source addresses are visible directly rather than hidden behind an untrusted proxy or shared NAT that would make a ban overly broad.
  • Nginx error logs contain genuine HTTP authentication failures at the configured path and are rotated without losing backend access.
  • Trusted management addresses are stable, minimal, and reviewed; the example documentation range is replaced before use.
  • Console or a second independent management path remains available throughout testing.

Key concepts

Jail
The operational policy joining one filter, log backend, thresholds, and ban action.
Filter
Regular expressions that match failure evidence and identify the address to ban.
findtime
The rolling time window in which retries are counted.
maxretry
Number of matching failures allowed in findtime before the jail bans the source.
bantime
How long the action blocks a source unless explicitly changed or removed.
ignoreip
Explicit addresses and networks that the jail never bans; it is a safety control and potential blind spot.

Before you copy

Values used in this guide

{{managementCidr}}

Smallest trusted management address or CIDR excluded from bans.

Example: 203.0.113.10/32
{{testIp}}

Disposable external source used for the controlled ban/unban drill.

Example: 198.51.100.27

Security and production boundaries

  • Fail2ban is rate-based reaction, not authentication hardening. Keep SSH keys, MFA where applicable, patched services, network policy, and strong web authentication.
  • A broad ignoreip range can exempt attackers. Prefer exact management addresses and audit every exception.
  • A shared NAT address may represent many legitimate users; banning it can create a denial of service.
  • Never run the controlled failure test from the only active administration source.
  • Treat custom filters as security code: an incorrect address capture can ban a proxy, load balancer, or innocent client.

Stop before continuing if

  • Stop if the real client address is not trustworthy or is hidden behind shared infrastructure.
  • Stop if fail2ban-regex matches successful or unrelated events, misses representative failures, or captures the wrong address.
  • Stop if configuration test fails or the selected action conflicts with the host firewall.
  • Stop if current management access is in the possible test source range without console recovery.
  • Do not widen ignoreip or disable the firewall globally to resolve one failed test.
01

command

Install Fail2ban

caution

Install the distribution package and keep vendor jail.conf unchanged.

Why this step matters

The Ubuntu package integrates service files, default filters, actions, and Python dependencies with host updates while leaving local policy for a separate override.

What to understand

Install from the configured signed distribution repositories and record candidate/version. Do not edit jail.conf because package upgrades can replace vendor defaults.

Installation may start or enable a service depending on packaging; inspect state before any jail can affect access.

System changes

  • Installs Fail2ban packages, unit, filters, actions, and default configuration; package scripts may create runtime state.

Syntax explained

apt update
Refreshes signed repository metadata.
apt install --yes fail2ban
Installs the distribution package non-interactively after repository review.
Command
sudo apt update && sudo apt install --yes fail2ban
Example output / evidence
Setting up fail2ban ...

Checkpoint: Record package and initial state

apt-cache policy fail2ban && systemctl is-enabled fail2ban; systemctl is-active fail2ban

Continue whenThe expected Ubuntu package is installed and any automatic service state is understood before local jails are enabled.

Stop whenPackage origin is unexpected or an existing active configuration has not been inventoried.

If this step fails

Fail2ban starts immediately with unknown jails.

Likely causePrevious local overrides remain on disk or another package installed configuration.

Safe checks
  • sudo fail2ban-client status
  • sudo find /etc/fail2ban -maxdepth 2 -type f -name '*.local' -o -name '*.conf' | sort

ResolutionStop, inventory, and preserve existing policy before merging this guide.

Security notes

  • Package installation does not make default thresholds appropriate for the host.

Alternatives

  • Use the vendor-supported package for another distribution and validate its paths/backends.

Stop conditions

  • Do not overwrite an existing active Fail2ban deployment without migration review.
02

config

Create local overrides

caution

List trusted sources precisely and start with measured thresholds.

Why this step matters

A local override makes trust, timing, and jail-specific backends explicit while preserving vendor filters and preventing the file-backed Nginx jail from inheriting the sshd journal backend.

What to understand

DEFAULT sets the smallest trusted management CIDR, one-hour ban, ten-minute window, and five retries. Replace the documentation example before activation.

sshd uses backend=systemd because Ubuntu authentication events are in journald. nginx-http-auth uses backend=auto and its exact error log path.

Thresholds begin conservatively and must be based on measured legitimate behavior, shared NAT, automation, and attack volume.

System changes

  • Creates /etc/fail2ban/jail.d/oneliners.local; no new ban occurs until validated configuration is loaded.

Syntax explained

ignoreip
Exempts loopback and the smallest reviewed management source.
bantime/findtime/maxretry
Defines one-hour bans after five failures inside ten minutes.
backend=systemd
Reads sshd evidence from the system journal.
backend=auto + logpath
Selects a file-capable backend for the exact Nginx error log.
File /etc/fail2ban/jail.d/oneliners.local
Configuration
Fill variables0/1 ready

Values stay on this page and are never sent or saved.

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 {{managementCidr}}
bantime = 1h
findtime = 10m
maxretry = 5

[sshd]
enabled = true
backend = systemd

[nginx-http-auth]
enabled = true
backend = auto
logpath = /var/log/nginx/error.log
Example output / evidence
Local jail override saved

Checkpoint: Inspect merged jail policy

sudo fail2ban-client -d | grep -E "\['(add|set)', '(sshd|nginx-http-auth)'" | head -n 40

Continue whenOnly intended jails, exact management CIDR, systemd ssh backend, and file-backed Nginx policy appear.

Stop whenThe example CIDR remains, an unexpected jail loads, or Nginx inherits systemd backend.

If this step fails

Configuration interpolation reports an unknown option.

Likely causeA directive is unsupported by the packaged Fail2ban version or placed in the wrong section.

Safe checks
  • fail2ban-client --version
  • sudo fail2ban-client --test

ResolutionUse the packaged manual and valid jail scope, then retest the full merge.

Security notes

  • Broad trusted networks create permanent blind spots; document every address owner.

Alternatives

  • Use per-jail ignoreip when web and SSH administration sources differ.

Stop conditions

  • No activation with placeholder trust ranges.
03

verification

Test filters against real logs

read-only

Inspect matched and missed lines before enabling a jail.

Why this step matters

Testing against real positive and negative logs demonstrates what the jail will count and whom it will ban before a regex gains firewall effects.

What to understand

fail2ban-regex applies the packaged sshd filter to the system journal and reports matched, ignored, and missed lines. Preserve sanitized fixtures for future upgrades.

Review captured addresses, not just total counts. Successful authentication, health checks, unrelated errors, proxy addresses, and IPv6 examples must remain unmatched.

Run a separate test for Nginx using its exact log file and nginx-http-auth filter.

System changes

  • No firewall or jail change; reads authentication evidence and computes filter results.

Syntax explained

systemd-journal
Uses the Fail2ban systemd journal input source.
sshd
Selects the packaged sshd filter definition.
--print-all-matched
Prints every matching line for manual address and context review.
Command
sudo fail2ban-regex systemd-journal sshd --print-all-matched
Example output / evidence
Failregex: 18 total
Lines: 122, 18 matched

Checkpoint: Approve positive and negative evidence

sudo fail2ban-regex systemd-journal sshd --print-all-matched

Continue whenRepresentative failures match with correct source addresses; successes and unrelated events do not.

Stop whenA legitimate line matches, a real failure is missed, or source extraction names a proxy/shared address incorrectly.

If this step fails

No lines match despite recent failed SSH logins.

Likely causeJournal selection, sshd unit naming, filter version, or event format differs.

Safe checks
  • journalctl -u ssh --since '-10 minutes' --no-pager
  • journalctl _COMM=sshd --since '-10 minutes' --no-pager
  • sudo fail2ban-client get sshd journalmatch

ResolutionAlign the journal match with actual Ubuntu events and rerun fixtures; do not broaden regex blindly.

Security notes

  • Sanitize usernames and addresses before sharing test fixtures.

Alternatives

  • Use a curated fixture file in CI plus a target-host journal smoke test.

Stop conditions

  • False positives block jail enablement.
04

verification

Validate configuration

read-only

Stop on any parse or interpolation error.

Why this step matters

The configuration test resolves includes, interpolation, jail backends, filters, log paths, and actions before the daemon changes firewall state.

What to understand

fail2ban-client --test parses the complete installed configuration. A success line and zero exit code are required.

Also inspect fail2ban-client -d output for the exact action and backend; syntactically valid configuration can still select the wrong firewall family.

Repeat after every package, filter, action, log, proxy, or firewall change.

System changes

  • No intended persistent change; validates the merged Fail2ban configuration.

Syntax explained

fail2ban-client --test
Parses the complete server configuration without starting the service.
Command
sudo fail2ban-client --test
Example output / evidence
OK: configuration test is successful

Checkpoint: Require a clean configuration

sudo fail2ban-client --test; echo $?

Continue whenConfiguration test successful and exit status 0.

Stop whenAny parse, interpolation, backend, logpath, filter, or action error appears.

If this step fails

Test reports no accessible log file for nginx-http-auth.

Likely causeThe log path is wrong, rotation has not created it, or backend inherited systemd.

Safe checks
  • sudo namei -l /var/log/nginx/error.log
  • sudo fail2ban-client -d | grep -A10 nginx-http-auth

ResolutionCorrect backend=auto and the real Nginx error path, then repeat regex and config tests.

Security notes

  • Do not start with `--test` findings merely because sshd jail alone appears valid.

Alternatives

  • Disable the unready web jail explicitly and deploy sshd alone with documented scope.

Stop conditions

  • Any validation error blocks start/restart.
05

command

Start and inspect jails

caution

Enable Fail2ban and confirm only intended jails are loaded.

Why this step matters

Starting and inspecting exact loaded jails proves the daemon accepted policy and lets operators catch unexpected firewall actions before controlled abuse testing.

What to understand

enable --now adds boot persistence and starts the service. status must list only sshd and nginx-http-auth in the intended rollout.

Inspect each jail's filter, action, retry values, ignore list, and current bans, then inspect nftables/iptables ownership.

Keep console access until permitted management traffic and ordinary service authentication remain healthy.

System changes

  • Enables and starts Fail2ban; active jails begin monitoring and may add temporary firewall rules when thresholds are met.

Syntax explained

systemctl enable --now
Enables at boot and starts Fail2ban immediately.
fail2ban-client status
Lists active jails and server state.
Command
sudo systemctl enable --now fail2ban && sudo fail2ban-client status
Example output / evidence
Jail list: nginx-http-auth, sshd

Checkpoint: Confirm intended live policy

sudo fail2ban-client status && sudo fail2ban-client status sshd && sudo fail2ban-client status nginx-http-auth

Continue whenExactly two intended jails are active with zero unexplained bans and correct log/journal sources.

Stop whenUnexpected jails or bans appear, management fails, or action errors reach the journal.

If this step fails

Fail2ban starts then exits.

Likely causeA runtime backend, database, socket, filter, log permission, or action error passed static parsing.

Safe checks
  • systemctl status fail2ban --no-pager
  • journalctl -u fail2ban -n 100 --no-pager
  • sudo fail2ban-client --test

ResolutionDisable only the faulty jail if necessary, correct its concrete error, validate, and restart.

Security notes

  • Watch existing firewall rules before and after start to detect backend conflicts.

Alternatives

  • Enable one jail at a time and observe before adding the next.

Stop conditions

  • Do not continue when current administration is degraded.
06

warning

Trigger a controlled ban

caution

Generate failures only from a disposable source, never the current management path.

Why this step matters

A disposable-source drill proves event generation, filter counting, threshold, firewall action, status, and continued recovery access without risking the operator's only session.

What to understand

Use a host whose address is testIp, not included in ignoreip, and safe to lose temporarily. Keep console and a separate approved SSH session open.

Generate exactly the measured number of failed authentications without sending real credentials. Watch the jail log and status from the safe session.

After ban, confirm the test source is blocked while the management source still works. Do not test against a shared NAT or production client address.

System changes

  • Creates one temporary Fail2ban-managed firewall ban for the explicit disposable test address.

Syntax explained

maxretry
Determines how many controlled failure matches trigger the test ban.
findtime
Requires failures to occur within the configured counting window.
bantime
Determines automatic expiry if explicit unban is not used.
Example output / evidence
Test address appears in Banned IP list; administration remains connected

Checkpoint: Prove ban and safe administration

sudo fail2ban-client status sshd

Continue when198.51.100.27 appears in Banned IP list, its new SSH connection fails, and independent administration remains available.

Stop whenA different address is banned, no console exists, shared users are affected, or firewall action is unclear.

If this step fails

Failures increment but the test address is not blocked.

Likely causeAction failed, wrong address family/path is active, or test did not cross maxretry inside findtime.

Safe checks
  • sudo fail2ban-client status sshd
  • journalctl -u fail2ban --since '-10 minutes' --no-pager
  • sudo nft -a list ruleset

ResolutionInspect action and timing; do not increase failure volume or broaden the ban until exact behavior is understood.

Security notes

  • Never generate the test from the production management source or with a valid account password.

Alternatives

  • Use a disposable network namespace/VM with its own source address.

Stop conditions

  • Abort immediately if any legitimate management path is affected.
07

command

Verify recovery

caution

Unban the test address and confirm the firewall rule disappears.

Why this step matters

Explicit unban proves the documented recovery control, daemon socket, jail selection, and firewall cleanup before operators depend on the policy.

What to understand

Target one jail and exact address. Return value 1 means a ban was removed; 0 often means it was not present.

Confirm the address leaves jail status and the Fail2ban-owned firewall set/chain, then verify connectivity from the disposable source.

Unban does not add the address to ignoreip; future qualifying failures can ban it again.

System changes

  • Removes the exact test address from the sshd jail and its active firewall ban.

Syntax explained

set sshd
Targets only the SSH jail.
unbanip {{testIp}}
Removes one exact source address from that jail.
Command
Fill variables0/1 ready

Values stay on this page and are never sent or saved.

sudo fail2ban-client set sshd unbanip {{testIp}}
Example output / evidence
1

Checkpoint: Verify complete recovery

sudo fail2ban-client status sshd && sudo nft -a list ruleset | grep -i f2b

Continue whenThe test address is absent from jail and firewall state, and its connection path recovers.

Stop whenA different address is removed, firewall state remains, or connectivity still fails for another reason.

If this step fails

unbanip returns 0 while the client remains blocked.

Likely causeAnother jail, firewall layer, address family, or upstream control owns the block.

Safe checks
  • sudo fail2ban-client status
  • sudo nft -a list ruleset
  • sudo iptables-save | grep -i 198.51.100.27

ResolutionIdentify the actual owner of the block; do not flush all firewall rules.

Security notes

  • Log emergency unbans and investigate the events that caused them.

Alternatives

  • Wait for short test bantime expiry while observing removal, if access is not urgent.

Stop conditions

  • Never flush an entire Fail2ban chain to recover one address.
08

verification

Review false positives

read-only

Observe jail status and logs before tuning thresholds.

Why this step matters

A post-rollout observation window distinguishes useful abuse reduction from false positives, action errors, log gaps, and noisy thresholds.

What to understand

Review currently/total failed and banned counts per jail, recent journal actions, service restarts, and legitimate authentication support reports.

Correlate spikes with proxy changes, scanners, deployments, shared NAT, monitoring, and credential rotation before tuning.

Change one parameter at a time through a reviewed local override, validate, reload/restart, and repeat filter fixtures.

System changes

  • No intended state change; reads jail counters and service journal for operational review.

Syntax explained

status sshd
Shows current filter and action counters plus banned addresses for the SSH jail.
journalctl -u fail2ban --since today
Reads today's daemon decisions and action errors.
--no-pager
Produces bounded noninteractive output for review.
Command
sudo fail2ban-client status sshd && journalctl -u fail2ban --since today --no-pager
Example output / evidence
Currently banned: 0

Checkpoint: Close the observation window

sudo fail2ban-client status sshd && sudo fail2ban-client status nginx-http-auth && journalctl -u fail2ban --since today --no-pager

Continue whenNo legitimate address is banned, action errors are absent, counters are plausible, and both log sources remain current.

Stop whenFalse positives, missing events, repeated daemon restarts, or action failures appear.

If this step fails

Ban counts surge after a reverse-proxy change.

Likely causeLogs now record the proxy address or trust headers incorrectly, collapsing clients into one identity.

Safe checks
  • sudo fail2ban-regex /var/log/nginx/error.log nginx-http-auth --print-all-matched
  • sudo tail -n 100 /var/log/nginx/error.log

ResolutionRestore trustworthy client-address logging and validate negative/positive fixtures before re-enabling the affected jail.

Security notes

  • Restrict access to ban telemetry that contains client addresses and account names.

Alternatives

  • Export aggregate jail metrics to the protected monitoring system.

Stop conditions

  • Disable only the faulty jail if its false positives cannot be corrected promptly.

Finish line

Verification checklist

Configurationsudo fail2ban-client --testReports success.
Jailssudo fail2ban-client statusLists intended jails.
Recoverysudo fail2ban-client set sshd unbanip TEST_IPReturns 1 for the banned test address.

Recovery guidance

Common problems and safe checks

The sshd jail is active but never counts known failures.

Likely causeThe systemd journal match, backend, service unit name, timestamp window, or distribution filter differs.

Safe checks
  • journalctl -u ssh --since '-10 minutes' --no-pager
  • sudo fail2ban-client get sshd journalmatch
  • sudo fail2ban-regex systemd-journal sshd --print-all-missed

ResolutionAlign the jail with the actual journal unit and validated distribution filter; do not invent a broad match against arbitrary log text.

The Nginx jail reports no accessible log file.

Likely causeA global systemd backend was incorrectly applied to a file jail, logpath differs, or permissions/rotation are wrong.

Safe checks
  • sudo fail2ban-client -d | grep -A8 nginx-http-auth
  • sudo namei -l /var/log/nginx/error.log
  • sudo tail -n 50 /var/log/nginx/error.log

ResolutionUse backend=auto for the file-backed jail, correct the exact path, and rerun filter plus configuration tests.

An administrator is banned unexpectedly.

Likely causeThe management source changed, shared NAT accumulated retries, or ignoreip is incomplete.

Safe checks
  • sudo fail2ban-client status sshd
  • sudo fail2ban-client get sshd ignoreip
  • journalctl -u fail2ban --since '-1 hour' --no-pager

ResolutionUse console or an independent source to unban the exact address, verify logs and identity, then make the narrowest evidence-based policy change.

Fail2ban shows a ban but the source can still connect.

Likely causeThe wrong banaction/address family is selected, traffic takes another firewall path, or a container/cloud rule bypasses the action.

Safe checks
  • sudo fail2ban-client get sshd banaction
  • sudo nft -a list ruleset
  • sudo iptables-save | grep -i f2b

ResolutionSelect and test the action that integrates with the active firewall and both required address families.

After the procedure

Alternatives and next steps

Consider these alternatives

  • Use upstream reverse-proxy or identity-provider rate limiting where the true client address and authentication context are stronger.
  • Use nftables rate limits for simple protocol-level abuse, while recognizing they do not parse authentication logs.
  • Use an external abuse-control service when attacks span many hosts and centralized correlation is required.

Operate it safely

  • Version local jail and filter overrides and test them against sanitized positive and negative log fixtures.
  • Add monitoring for active jails, repeated bans, failures to start, action errors, and sudden false-positive growth.
  • Review ignoreip, thresholds, actions, and service log formats after every authentication or proxy change.
  • Integrate ban events with incident telemetry without publishing sensitive source data unnecessarily.
  • Rehearse console unban and jail rollback on every supported host/firewall variant.

Reference

Frequently asked questions

Should backend=systemd be placed in DEFAULT?

Not when file-backed jails are also enabled. Set systemd for sshd and auto for the Nginx log jail so each reads the evidence it actually produces.

Can Fail2ban replace key-only SSH?

No. It reduces repeated attempts after they appear in logs; strong authentication and least exposure prevent compromise more directly.

Why test both matched and missed lines?

Counting positive matches alone can hide a filter that also bans successful users or captures the wrong address. Negative fixtures measure false positives.

Recovery

Rollback

Disable the problematic jail and remove its rules.

  1. Unban affected administrators from console.
  2. Disable the jail in the local override.
  3. Validate and restart Fail2ban.
  4. Confirm Fail2ban-owned firewall chains match the remaining jails.

Evidence

Sources and review

Verified 2026-07-24Review due 2026-10-22
Fail2ban projectofficialjail.conf manualofficialfail2ban-client manualofficial