Fix dovecot low-and-slow fail2ban gap; generalize findtime guidance
Same evasion pattern already fixed for sshd recurred against dovecot: a coordinated IMAP password spray paced just outside the 30-minute findtime window ran unbanned for days. Widened dovecot's findtime the same way, and updated SETUP.md so future provisioning applies this to every credential jail up front instead of waiting for it to reappear. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -506,14 +506,19 @@ Enable jails for `sshd`, `postfix`, `postfix-sasl`, `postfix-rbl`,
|
|||||||
`/etc/fail2ban/jail.local`, all watching `/var/log/mail.log` /
|
`/etc/fail2ban/jail.local`, all watching `/var/log/mail.log` /
|
||||||
`/var/log/auth.log` / `/var/log/apache2/*.log` as appropriate.
|
`/var/log/auth.log` / `/var/log/apache2/*.log` as appropriate.
|
||||||
|
|
||||||
**Widen `sshd`'s `findtime` beyond the 30-minute default.** Default
|
**Widen `findtime` beyond the 30-minute default on every credential-auth
|
||||||
`findtime=30m`/`maxretry=3` only catches bursts — a bot that spaces login
|
jail, not just `sshd`.** Default `findtime=30m`/`maxretry=3` only catches
|
||||||
attempts ~33 minutes apart never accumulates 3 strikes inside any 30-minute
|
bursts — a bot that spaces login attempts ~22-33 minutes apart never
|
||||||
window and is never banned at all (seen live on this host against
|
accumulates 3 strikes inside any 30-minute window and is never banned at
|
||||||
`47.76.192.176`; check `fail2ban-client status sshd` and grep
|
all. Seen live on this host twice with the *identical* pacing trick:
|
||||||
`/var/log/fail2ban.log` for `Found`/`Ban` counts per IP if a repeat
|
first against `sshd` (`47.76.192.176`, ~33min spacing), then — because
|
||||||
offender never shows up banned). Fix in its own drop-in rather than
|
only `sshd` got fixed at the time — against `dovecot`
|
||||||
editing the packaged jail file:
|
(`47.95.196.60`/`82.157.132.42`/`120.48.118.132`/others, ~22-25min
|
||||||
|
spacing, coordinated multi-IP campaign, ran for days unbanned). Don't
|
||||||
|
wait for it to recur in a third jail: apply the same override to every
|
||||||
|
jail guarding a real credential (`sshd`, `dovecot`, `postfix-sasl`,
|
||||||
|
`sieve`, `courier-auth` if in use) at provisioning time, each in its own
|
||||||
|
drop-in rather than editing the packaged jail file:
|
||||||
```
|
```
|
||||||
# /etc/fail2ban/jail.d/sshd-findtime.local
|
# /etc/fail2ban/jail.d/sshd-findtime.local
|
||||||
[sshd]
|
[sshd]
|
||||||
@@ -521,10 +526,23 @@ findtime = 1d
|
|||||||
maxretry = 4
|
maxretry = 4
|
||||||
bantime = 1d
|
bantime = 1d
|
||||||
```
|
```
|
||||||
`fail2ban-client reload sshd` applies it. Worth spot-checking the other
|
```
|
||||||
jails' `Found`/`Ban` ratios the same way — a *distributed* variant (many
|
# /etc/fail2ban/jail.d/dovecot-findtime.local
|
||||||
different source IPs, each trying once) evades per-IP banning by design
|
[dovecot]
|
||||||
and needs subnet-level blocking instead, which `findtime` tuning can't fix.
|
findtime = 1d
|
||||||
|
maxretry = 4
|
||||||
|
bantime = 1d
|
||||||
|
```
|
||||||
|
`fail2ban-client reload <jail>` applies each one. To check whether a jail
|
||||||
|
still has the gap: `fail2ban-client status <jail>` for who's currently
|
||||||
|
banned, cross-referenced against grepping the jail's own log
|
||||||
|
(`/var/log/auth.log`, `/var/log/mail.log`, etc.) for per-IP hit counts —
|
||||||
|
if an IP has dozens of failures spread evenly a few minutes past
|
||||||
|
`findtime` apart and never shows up banned, that's this pattern. Also
|
||||||
|
worth spot-checking the Apache jails' `Found`/`Ban` ratios the same way —
|
||||||
|
a *distributed* variant (many different source IPs, each trying once)
|
||||||
|
evades per-IP banning by design and needs subnet-level blocking instead,
|
||||||
|
which `findtime` tuning can't fix.
|
||||||
|
|
||||||
### Bring it up
|
### Bring it up
|
||||||
|
|
||||||
@@ -706,10 +724,11 @@ Run through this after everything above is live:
|
|||||||
- [ ] Disk partition actually uses the full disk (`df -h /` vs. `lsblk`/`parted ... print free` should agree)
|
- [ ] Disk partition actually uses the full disk (`df -h /` vs. `lsblk`/`parted ... print free` should agree)
|
||||||
- [ ] `PermitRootLogin no` / `PasswordAuthentication no` — verify live with
|
- [ ] `PermitRootLogin no` / `PasswordAuthentication no` — verify live with
|
||||||
`sshd -T`, not just the config file (see §1)
|
`sshd -T`, not just the config file (see §1)
|
||||||
- [ ] `fail2ban-client status sshd` findtime is wide enough to catch
|
- [ ] `fail2ban-client status <jail>` findtime is wide enough to catch
|
||||||
low-and-slow attempts, not just bursts (see §3) — spot-check
|
low-and-slow attempts, not just bursts, for every credential jail
|
||||||
`/var/log/fail2ban.log` for any repeat-offender IP with several
|
(`sshd`, `dovecot`, `postfix-sasl`, not just `sshd`) — see §3 —
|
||||||
`Found` entries but no `Ban`
|
spot-check `/var/log/fail2ban.log` for any repeat-offender IP with
|
||||||
|
several `Found` entries but no `Ban`
|
||||||
- [ ] Any chrooted SFTP accounts (`/var/sftp/*`) no longer in active use are
|
- [ ] Any chrooted SFTP accounts (`/var/sftp/*`) no longer in active use are
|
||||||
password-locked (`sudo passwd -S <user>` shows `L`), not left active
|
password-locked (`sudo passwd -S <user>` shows `L`), not left active
|
||||||
|
|
||||||
|
|||||||
@@ -236,3 +236,25 @@ Nothing here is urgent; all are low-risk, no-downtime changes.
|
|||||||
rather than editing the Debian-packaged jail file. Applied via
|
rather than editing the Debian-packaged jail file. Applied via
|
||||||
`fail2ban-client reload sshd`; `47.76.192.176` was banned on its very
|
`fail2ban-client reload sshd`; `47.76.192.176` was banned on its very
|
||||||
next attempt after the change.
|
next attempt after the change.
|
||||||
|
|
||||||
|
- [x] **fail2ban: `dovecot` jail had the same low-and-slow gap as `sshd`** — done 2026-07-25
|
||||||
|
found while auditing which IPs were hitting webmail hard enough to
|
||||||
|
deserve a ban but weren't in any currently-banned list. Same default
|
||||||
|
`findtime=30m`/`maxretry=3` as `sshd`'s pre-fix config. At least 4 IPs
|
||||||
|
(`47.95.196.60` 167 hits, `82.157.132.42` 144, `120.48.118.132` 76,
|
||||||
|
`188.164.195.116` 57, `157.245.38.26` 34) ran a coordinated IMAP
|
||||||
|
password spray against generic mailbox names (`admin@`, `test@`,
|
||||||
|
`support@`, `sales@jayfield.org`) starting almost simultaneously around
|
||||||
|
2026-07-21 22:40-23:38, each pacing itself at ~1 attempt/22-25min —
|
||||||
|
just outside the 30-minute window, so none ever tripped 3 strikes.
|
||||||
|
`120.48.118.132` was still active as late as 2026-07-25 13:32, so the
|
||||||
|
campaign was ongoing, not historical. `postfix-sasl` shares the same
|
||||||
|
default thresholds and mail stack but showed much lower per-IP volume
|
||||||
|
in this pass — worth a follow-up check if it starts trending the same
|
||||||
|
way.
|
||||||
|
Fixed: `/etc/fail2ban/jail.d/dovecot-findtime.local`, same values as
|
||||||
|
the `sshd` fix (`findtime=1d`, `maxretry=4`, `bantime=1d`). Applied via
|
||||||
|
`fail2ban-client reload dovecot`; verified live with
|
||||||
|
`fail2ban-client get dovecot findtime/maxretry/bantime` and confirmed
|
||||||
|
the jail's existing ban state (`47.88.56.201`, 29 total bans) survived
|
||||||
|
the reload.
|
||||||
|
|||||||
Reference in New Issue
Block a user