Skip to content

10 Fail2Ban SSH Mail FTP

Mikhail Deynekin edited this page Oct 20, 2026 · 2 revisions

Fail2Ban: SSH, Mail, FTP

Service jails differ from web jails: the evidence is authentication failures, the thresholds must tolerate humans, and a false positive can lock out an administrator or a mail client.


SSH

Why it matters

Measured on the reference host over ~3.5 days: 8,070 Failed password and 3,581 Invalid user events from 507 unique IPs, with usernames targeted by name (ubuntu, admin, and even the hostname and domain names of the server). SSH is the most attacked service on any public Linux host.

Jail configuration (journal backend)

[sshd]
enabled      = true
backend      = systemd
journalmatch = _SYSTEMD_UNIT=ssh.service + _COMM=sshd
maxretry     = 4
findtime     = 10m
bantime      = 1d
port         = ssh              # ban TCP/22 only
action       = %(action_)s
               %(action_abuseipdb_async)s

Notes:

  • backend = systemd avoids depending on /var/log/auth.log (some hosts log to journal only);
  • maxretry = 4 in 10 minutes: humans mistype a key/password a couple of times, bots hit dozens;
  • bantime = 1d with bantime.increment = true grows repeat offenders up to bantime.maxtime;
  • port = ssh keeps the firewall ban scoped to SSH.

Testing the filter

# if auth.log exists
fail2ban-regex /var/log/auth.log /etc/fail2ban/filter.d/sshd.conf | tail -5

# journal-only host: export a sample first
journalctl -u ssh --since "-2 hours" --no-pager > /tmp/ssh-sample.log
fail2ban-regex /tmp/ssh-sample.log /etc/fail2ban/filter.d/sshd.conf | tail -5

Expect matches on lines like:

sshd-session[123]: Failed password for root from 198.51.100.7 port 44432 ssh2
sshd-session[123]: Invalid user ubuntu from 198.51.100.7 port 47192

Hardening sshd itself (do this before aggressive bans)

sshd -T | grep -Ei '^(passwordauthentication|permitrootlogin|maxauthtries|kbdinteractiveauthentication)'

Recommended target:

Setting Recommended Why
PermitRootLogin prohibit-password (keys only) password guessing against root is the main attack
PasswordAuthentication no (if all users have keys) removes the whole class of password brute force
MaxAuthTries 3–4 fail2ban sees the failures sooner
KbdInteractiveAuthentication no avoids PAM password fallback

⚠️ Do not lock yourself out. Keep an existing SSH session open, have a second terminal or out-of-band console, edit /etc/ssh/sshd_config.d/*.conf, run sshd -t, then reload (systemctl reload ssh). Never test ban rules with your own management IP.

recidive: repeat offenders

[recidive]
enabled   = true
logpath   = /var/log/fail2ban.log
findtime  = 1d
maxretry  = 5
bantime   = 4w
banaction = nftables[type=allports]

Exclude it from reporting (skip_jails = recidive): the same IP was already reported by the specific jail; recidive would only create duplicates.


Exim (SMTP AUTH)

What the log shows

/var/log/exim4/mainlog:

2026-01-02 03:04:05 auth_login authenticator failed for H=(localhost) [198.51.100.7]: 535 Incorrect authentication data
2026-01-02 03:04:06 auth_login authenticator failed for H=(localhost) [198.51.100.7]: 435 Unable to authenticate at present: authentication socket read error or premature eof

The first line is a client failure (535). The second is not a client failure: 435 means the auth backend could not be reached (for example Dovecot auth is down or its socket is misconfigured). Never put 435 into a fail2ban filter — during a backend outage it bans every legitimate mail client that tries to authenticate.

Measured on a production host: 7,196 435 lines in one day and six bans, all caused by a broken Dovecot auth process (CRAM-MD5 mechanism can't be supported with given passdbs with a PAM passdb). After fixing auth_mechanisms and removing 435 from the filter, the bans were lifted and genuine 535 attacks continued to be blocked.

Filter (535 only)

The stock exim-isp filter only matches 535, and that is correct. Keep the filter at 535:

# /etc/fail2ban/filter.d/exim-auth-fail.conf
[Definition]
# 535 = bad credentials (client abuse); 435 = auth backend fault (never match it).
failregex = ^\s*\S+ authenticator failed for .*?\[<HOST>\](?::\d+)?(?: I=\[\S+\](?::\d+)?)?: 535 Incorrect authentication data.*$
ignoreregex =

Jail:

[exim-auth-fail]
enabled  = true
filter   = exim-auth-fail
logpath  = /var/log/exim4/mainlog
maxretry = 15
findtime = 1h
bantime  = 1d
port     = 25,465,587

The threshold is deliberately generous: mail clients retry. If 435 appears in the log, do not filter it — diagnose the auth path instead:

dovecot -n | grep auth_mechanisms
journalctl -u dovecot --since "-10 min" | grep -iE "fatal|auth"
systemctl status dovecot exim4

Fix the backend (for example remove cram-md5 when the passdb is PAM/passwd, then restart Dovecot). After that, only 535 (real bad credentials) reaches fail2ban.

Category mapping for this jail: the project maps it to 18 (Brute-Force); add 11 (Email Spam) only if you also observe spam delivery attempts.

Testing

fail2ban-regex /var/log/exim4/mainlog /etc/fail2ban/filter.d/exim-auth-fail.conf | tail -5

Dovecot (IMAP/POP3 brute force)

If the host serves mailboxes, IMAP brute force is a separate vector:

[dovecot]
enabled  = true
filter   = dovecot
logpath  = /var/log/mail.log
maxretry = 10
findtime = 10m
bantime  = 1d
port     = 110,143,993,995

The stock dovecot filter matches auth failed / aborted logins. Test on a sample:

fail2ban-regex /var/log/mail.log /etc/fail2ban/filter.d/dovecot.conf | tail -5

If the same failure already appears in Exim's log, keep both jails but exclude one from reporting to avoid duplicates (or accept one report per 20 minutes — the worker aggregates by IP anyway).


ProFTPD

[proftpd]
enabled  = true
filter   = proftpd
logpath  = /var/log/proftpd/proftpd.log
maxretry = 5
findtime = 10m
bantime  = 1d
port     = 20,21,989,990

Measured: 3,995 failed logins in one day from two IPs. But 2,880 of them came from the owner's own IP with a wrong username (cam_*: no such user). Always whitelist the administrator's address before enabling this jail:

[DEFAULT]
ignoreip = 127.0.0.1/8 ::1 203.0.113.10   # ...your office/owner IP

Test with the stock filter:

fail2ban-regex /var/log/proftpd/proftpd.log /etc/fail2ban/filter.d/proftpd.conf | tail -5

Policy matrix

Exposure SSH Web Mail FTP
Minimal / small VPS maxretry 4 / 10m / 1d off off off
Typical public host + recidive 4w attack-path + PHP probes SMTP AUTH 15/1h 5/10m + owner whitelist
Aggressive + key-only SSH, custom port + nginx-forbidden, rate limits + Dovecot only if FTP is really needed

Aggressive settings increase the risk of locking out legitimate users; always keep ignoreip current.

Verification per service

# jail exists and has the actions
fail2ban-client get sshd actions
fail2ban-client get exim-auth-fail actions
fail2ban-client get proftpd actions

# thresholds and current state
fail2ban-client get sshd maxretry
fail2ban-client status sshd

# the firewall really contains the address
nft list set inet f2b-table addr-set-sshd

Hardening beyond fail2ban

Fail2ban reduces noise; it does not fix weak authentication:

  • SSH: key-only auth, no root password, limited users, AllowUsers/AllowGroups;
  • Mail: strong passwords, SPF/DKIM/DMARC, TLS, rate limits at the MTA;
  • FTP: prefer SFTP/SSH, or restrict port 21 to trusted networks;
  • Web: WAF/CDN rules for volumetric attacks; fail2ban handles the long tail.

Next: Web Protection · Verification & Rollback · Agent Prompts: Fail2Ban Setup

Clone this wiki locally