给 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, sshdbackend 与 banaction 是两个不同的设置:
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.conf、jail.d/*.conf、jail.local、jail.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 = ufwPostfix 和 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 fail2ban5. 验证日志、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:
ufw6. 做一次端到端测试
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 规则;第三条命令会立即删除测试封禁。