# 给 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：

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

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

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

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

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

`backend` 与 `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`：

```ini
[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]` 中设置：

```ini
[DEFAULT]
banaction = ufw
banaction_allports = ufw
```

若只想切换 SSH jail，也可以只写：

```ini
[sshd]
banaction = ufw
```

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

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

## 4. 先检查配置，再重启 jail

编辑完成后先解析配置：

```bash
sudo fail2ban-client -d >/dev/null
```

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

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

```bash
sudo systemctl restart fail2ban
```

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

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

```bash
sudo fail2ban-client status postfix-sasl
sudo fail2ban-client status postfix
```

输出中的 `File list` 应为：

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

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

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

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

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

本次实际输出：

```text
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. 做一次端到端测试

```bash
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 规则；第三条命令会立即删除测试封禁。

## 参考

- [Fail2Ban 1.1.0 UFW action](https://github.com/fail2ban/fail2ban/blob/1.1.0/config/action.d/ufw.conf)
- [Fail2Ban 1.1.0 默认 jail 配置](https://github.com/fail2ban/fail2ban/blob/1.1.0/config/jail.conf)


---

> 作者: Nite  
> URL: https://www.nite07.com/zh-cn/posts/fail2ban-postfix-podman/  

