给 Podman 邮局配置 Fail2Ban:用 UFW 封禁 SASL 暴力破解

目录

我的自建邮局(Postfix + Dovecot)跑在 netcup VPS 的 rootless Podman 容器里。查看 Postfix 日志摘要时,24 小时内有 2477 封邮件被拒,其中 2471 封来自同一 IP;同时出现了 152 次 SASL LOGIN 认证失败,以及扫描、中继滥用和 TLS 探测。

Postfix 自己可以拒绝垃圾邮件,但 SASL 密码猜测是另一类问题。Fail2Ban 能从日志里识别重复失败的来源,再让防火墙封禁 IP。服务器已经用 UFW 管理入站规则,因此这里不让 Fail2Ban 直接写 iptables,而是使用它内置的 ufw action。

环境

项目
宿主机 Arch Linux
容器运行时 Podman rootless
邮局镜像 ghcr.io/docker-mailserver/docker-mailserver:latest
邮局服务 systemd-mailserver
Quadlet 文件 ~/pod/docker-mailserver/mailserver.container
宿主机邮件日志 ~/pod/docker-mailserver/logs/mail.log
容器内邮件日志 /var/log/mail/mail.log
Fail2Ban 1.1.0
防火墙 UFW

1. 安装并检查现有状态

安装并启动 Fail2Ban:

sudo pacman -S fail2ban
sudo systemctl enable --now fail2ban

先确认 UFW 已启用、Fail2Ban 正在运行,以及当前启用的 jail:

sudo ufw status verbose
sudo systemctl is-active fail2ban
sudo fail2ban-client status

这里应看到 UFW 的 Status: active。完成配置后,jail 列表会包含:

Status
|- Number of jail:  3
`- Jail list:       postfix, postfix-sasl, sshd

backendbanaction 是两个不同的设置:

  • backend 决定 Fail2Ban 从哪里读取失败记录,例如 journald 或普通日志文件。
  • banaction 决定命中阈值后怎样封禁 IP。

这个邮件容器将日志写到宿主机挂载文件,所以邮件 jail 要使用 backend = polling;切换到 UFW 不会改变日志读取方式。

2. 让 Postfix jail 读取容器日志

传统安装的 Postfix 日志通常在 /var/log/mail.log。容器里的这个路径对宿主机 Fail2Ban 不可见,应该填写宿主机 Volume 挂载后的真实路径。

Arch 的 Fail2Ban 默认会通过 paths-arch.conf 为 Postfix 选择 systemd 日志后端。这里的日志是文件,因此在本地覆盖配置里明确设置 polling

创建或编辑 /etc/fail2ban/jail.d/mailserver.local

[DEFAULT]
postfix_backend = polling
postfix_log = /home/nite/pod/docker-mailserver/logs/mail.log

[postfix-sasl]
enabled   = true
mode      = auth
port      = smtp,465,submission,imap,imaps,pop3,pop3s
logpath   = /home/nite/pod/docker-mailserver/logs/mail.log
backend   = polling
maxretry  = 5
findtime  = 10m
bantime   = 1h
banaction = ufw

[postfix]
enabled   = true
mode      = more
port      = smtp,465,submission
logpath   = /home/nite/pod/docker-mailserver/logs/mail.log
backend   = polling
maxretry  = 5
findtime  = 10m
bantime   = 1h
banaction = ufw

文件后缀要用 .local。Fail2Ban 依次读取 jail.confjail.d/*.confjail.localjail.d/*.local,这个本地文件会在最后覆盖默认值。

3. 将 sshd 的封禁 action 改为 UFW

不要编辑 /etc/fail2ban/jail.conf,它由软件包提供,更新时可能被覆盖。

如果希望默认 jail 都使用 UFW,编辑 /etc/fail2ban/jail.local,在 [DEFAULT] 中设置:

[DEFAULT]
banaction = ufw
banaction_allports = ufw

若只想切换 SSH jail,也可以只写:

[sshd]
banaction = ufw

Postfix 和 postfix-sasl 已在 mailserver.local 中各自指定 banaction = ufw,不会依赖这个全局设置。

Fail2Ban 自带的 UFW action 会调用 ufw 添加和删除规则。这样不需要再通过 iptables -L 查 Fail2Ban 的封禁链,直接查看 UFW 即可。

4. 先检查配置,再重启 jail

编辑完成后先解析配置:

sudo fail2ban-client -d >/dev/null

这条命令只检查解析结果,不修改防火墙。

切换 action 后应完整重启服务:

sudo systemctl restart fail2ban

5. 验证日志、action 和 UFW 规则

确认邮件 jail 正在读取宿主机日志:

sudo fail2ban-client status postfix-sasl
sudo fail2ban-client status postfix

输出中的 File list 应为:

/home/nite/pod/docker-mailserver/logs/mail.log

不实际封禁的情况下,可以测试 SASL 过滤器是否匹配已有日志:

sudo fail2ban-regex /home/nite/pod/docker-mailserver/logs/mail.log "postfix[mode=auth]"

服务重启后,检查运行时 action,而不是只看配置文件:

sudo systemctl is-active fail2ban
sudo fail2ban-client get sshd actions
sudo fail2ban-client get postfix actions
sudo fail2ban-client get postfix-sasl actions

本次实际输出:

active
The jail sshd has the following actions:
ufw
The jail postfix has the following actions:
ufw
The jail postfix-sasl has the following actions:
ufw

6. 做一次端到端测试

sudo fail2ban-client set sshd banip 192.0.2.1
sudo ufw status numbered | grep -F 192.0.2.1
sudo fail2ban-client set sshd unbanip 192.0.2.1

第二条命令应显示带有 by Fail2Ban 注释的 UFW 规则;第三条命令会立即删除测试封禁。

参考

目录