Post

Overview Mail Server Hardening

Overview Mail Server Hardening

Fail2Ban Hardening Guide

_Last updated: 2026-01-01

This document is the authoritative, consolidated reference for Fail2Ban on the Mail2 system. It includes configuration, AbuseIPDB integration, Rspamd interaction, recidive behavior, diagnostics, verification, and operational procedures.


1. Scope and Goals

  • Protect SSH, SMTP, SMTP AUTH, IMAP/POP3, and web authentication services
  • Enforce network-level blocking using nftables
  • Report high-confidence abuse to AbuseIPDB
  • Maintain performance and avoid false positives
  • Provide repeatable, auditable configuration
  • Explain expected and unexpected behaviors

2. System Overview

  • OS: Debian 13
  • Mail stack: Postfix, Dovecot, Rspamd
  • Web: Apache
  • Firewall: nftables
  • Logging: systemd journal + /var/log/fail2ban.log
  • Abuse reporting: AbuseIPDB

3. Fail2Ban Configuration Layout

Main configuration files:

1
2
/etc/fail2ban/jail.conf
/etc/fail2ban/jail.local

Per-jail overrides:

1
/etc/fail2ban/jail.d/

Enabled jails:

  • sshd (auto-enabled on Debian 13)
  • apache-auth
  • postfix
  • postfix-sasl
  • dovecot
  • recidive

4. Debian 13 SSH Behavior

On Debian 13, the sshd jail:

  • Is enabled automatically
  • Is defined in /etc/fail2ban/jail.local
  • Requires no jail.d/sshd.conf file

Recommended override:

1
2
3
[sshd]
maxretry = 3
bantime  = 12h

AbuseIPDB reporting for SSH is optional and often disabled to avoid noise.


5. Apache Authentication Jail

1
2
3
4
5
6
7
8
9
10
11
12
[apache-auth]
enabled = true
backend = systemd

findtime = 3m
maxretry = 5
bantime  = 10m

journalmatch = _SYSTEMD_UNIT=apache2.service

action = %(action_mw)s
         %(action_abuseipdb)s[abuseipdb_category="18"]

6. Postfix (SMTP Protocol Abuse)

1
2
3
4
5
6
7
8
9
10
11
12
[postfix]
enabled = true
backend = systemd

findtime = 10m
maxretry = 3
bantime  = 1h

journalmatch = _SYSTEMD_UNIT=postfix.service

action = %(action_mw)s
         %(action_abuseipdb)s[abuseipdb_category="22"]

7. Postfix SASL (SMTP AUTH)

1
2
3
4
5
6
7
8
9
10
11
12
[postfix-sasl]
enabled = true
backend = systemd

findtime = 10m
maxretry = 3
bantime  = 4h

journalmatch = _SYSTEMD_UNIT=postfix.service

action = %(action_mw)s
         %(action_abuseipdb)s[abuseipdb_category="18"]

8. Dovecot (IMAP / POP3)

1
2
3
4
5
6
7
8
9
10
11
12
[dovecot]
enabled = true
backend = systemd

findtime = 10m
maxretry = 4
bantime  = 2h

journalmatch = _SYSTEMD_UNIT=dovecot.service

action = %(action_mw)s
         %(action_abuseipdb)s[abuseipdb_category="18"]

9. Recidive Jail (Repeat Offenders)

1
2
3
4
5
6
7
[recidive]
enabled  = true
backend  = auto
logpath  = /var/log/fail2ban.log
findtime = 1d
maxretry = 5
bantime  = 7d

Important behavior:

  • Recidive bans do not persist across Fail2Ban restarts
  • An Unban shortly after a Ban usually indicates a restart
  • This is expected and documented behavior

10. AbuseIPDB Configuration

Main action file:

1
/etc/fail2ban/action.d/abuseipdb.conf
1
2
abuseipdb_apikey   = <REDACTED>
abuseipdb_category = 18

Verification:

1
fail2ban-client -d | grep abuseipdb

11. Rspamd Integration Philosophy

  • Rspamd evaluates message content and reputation
  • Fail2Ban blocks persistent abusive behavior
  • Never ban based on spam score alone

Rspamd is the judge. Fail2Ban is the bouncer.


12. Logging Locations

Fail2Ban:

1
/var/log/fail2ban.log

Rspamd:

1
/var/log/rspamd/rspamd.log

Firewall:

1
nft list ruleset

13. Operational Cheat Sheet

Restart Fail2Ban:

1
systemctl restart fail2ban

Check jail status:

1
2
fail2ban-client status
fail2ban-client status postfix-sasl

Manual ban/unban:

1
2
fail2ban-client set sshd banip 1.2.3.4
fail2ban-client set sshd unbanip 1.2.3.4

Appendix E – Verification & Diagnostics

This appendix documents how to prove Fail2Ban is behaving correctly and how to diagnose unexpected behavior.


E.1 Confirm Configured Ban Time (Authoritative)

1
fail2ban-client get <jail> bantime

Examples:

1
2
3
4
5
6
fail2ban-client get apache-auth bantime
fail2ban-client get postfix bantime
fail2ban-client get postfix-sasl bantime
fail2ban-client get dovecot bantime
fail2ban-client get sshd bantime
fail2ban-client get recidive bantime

Interpretation (seconds):

  • 600 = 10 minutes
  • 3600 = 1 hour
  • 14400 = 4 hours
  • 43200 = 12 hours
  • 604800 = 7 days

E.2 Verify Runtime Ban Duration (Ground Truth)

1
grep -E " \[<jail>\] (Ban|Unban) " /var/log/fail2ban.log

Example:

1
grep -E " \[apache-auth\] (Ban|Unban) " /var/log/fail2ban.log

Compare timestamps between Ban and Unban entries.


E.3 Check Active Bans and Remaining Time

1
2
fail2ban-client status <jail>
fail2ban-client get <jail> banip --with-time

Remaining time is shown in seconds.


E.4 Recidive Verification

Confirm recidive is file-backed:

1
fail2ban-client status recidive

Expected:

1
File list: /var/log/fail2ban.log

Check recidive ban history:

1
grep -E " \[recidive\] (Ban|Unban) " /var/log/fail2ban.log

E.5 Explain Unexpected Unbans

Unexpected early Unbans are almost always caused by a Fail2Ban restart.

Confirm restarts:

1
journalctl -u fail2ban --since "YYYY-MM-DD HH:MM" --until "YYYY-MM-DD HH:MM"

If a restart occurred during a ban window, the Unban is expected.


E.6 Firewall Enforcement Validation

1
nft list ruleset | grep -i fail2ban -A5
  • IP present → ban active
  • IP missing → ban expired or service restarted

E.7 Pre-Restart Configuration Validation

Always validate configuration before restarting Fail2Ban:

1
fail2ban-client -d >/dev/null
  • Silent return = configuration valid
  • Output = fix errors before restarting

E.8 Key Operational Truths

  • Configuration values ≠ runtime behavior
  • Logs are the source of truth
  • Recidive bans do not survive restarts
  • Normal jails may log Restore Ban events
  • Restart history explains most anomalies
This post is licensed under CC BY 4.0 by the author.