📑 目录
一、过滤器架构概览
Postfix 提供了四种内容过滤器(Content Filter)接入方式,分别对应邮件在 MTA 流水线中不同的处理时机。RFC 5321 定义的 SMTP 会话协议(EHLO/MAIL/RCPT/DATA/BDAT)在 Postfix 中被拆解为多个独立模块(cleanup、trivial-rewrite、qmgr、smtpd),过滤器可以插入在不同模块之间。
| 架构 | 接入点 | 配置指令 | 代表实现 |
|---|---|---|---|
| Milter | smtpd 对话中(DATA 后) | smtpd_milters | Rspamd, OpenDKIM, MIMEDefang |
| Before-queue | cleanup 前 | content_filter + smtpd_proxy_filter | Amavis (部分场景) |
| After-queue | qmgr 调度后 | content_filter | Amavis + ClamAV, MailScanner |
| Pre-queue (SMP) | smtpd 进程内 | smtpd_milter_header_checks | Postfix 内置 (< 3.0 实验性) |
二、Milter(Sendmail Milter 协议)
2.1 协议原理
Milter(Mail Filter)协议起源于 Sendmail(RFC 交付前由 Sendmail, Inc. 定义,后纳入 Postfix 支持),允许外部过滤器在 SMTP 会话的 DATA 阶段后、邮件入队前对邮件做检查和修改。Milter 通过 Unix 域套接字(推荐)或 TCP 套接字与 smtpd 进程通信。
Milter 可以执行以下操作:
SMFIR_ACCEPT — 放行 | SMFIR_REJECT — 在 SMTP 阶段拒绝 | SMFIR_DISCARD — 静默丢弃 | SMFIR_CHGFROM — 修改 MAIL FROM | SMFIR_ADDHEADER — 添加头部 | SMFIR_CHGHEADER — 修改头部 | SMFIR_REPLYCODE — 返回自定义 SMTP 状态码
2.2 Milter 配置
# /etc/postfix/main.cf — Milter 配置
smtpd_milters = unix:/run/rspamd/rspamd-milter.sock
unix:/run/opendkim/opendkim.sock
non_smtpd_milters = unix:/run/rspamd/rspamd-milter.sock
# Milter 超时控制(防止过滤器卡死阻塞 SMTP 会话)
milter_protocol = 6 # 协议版本,6 = v6 (RFC 草案)
milter_macro_daemon_name = $myhostname
milter_macro_v = $mail_name $mail_version
milter_command_timeout = 30s # 过滤器命令响应超时
milter_content_timeout = 300s # 内容体传输超时(大附件场景)
# 按域启用/禁用 milter
smtpd_milter_maps = hash:/etc/postfix/milter_domains
# /etc/postfix/milter_domains:
# example.com FILTER
# example.net FILTER
2.3 延迟特征
Milter 最大的优势是 before-queue 过滤——在邮件写入队列之前完成检查,一旦拒绝则完全不产生队列 I/O。但缺点是 Milter 的响应速度直接影响 SMTP 客户端(发件 MTA)的感知延迟,因为 SMTP 会话在等待 Milter 返回后才发送 250 OK。
RFC 5321 Section 4.5.3.2 要求 SMTP 客户端 DATA 阶段超时为 10 分钟——如果 Milter 耗时超过 SMTP 会话超时,发件方会断开重试,造成邮件重复入队。
三、Before-queue 过滤器
3.1 before-queue proxy 模式
Postfix 通过 smtpd_proxy_filter 指令在 smtpd 与 cleanup 之间嵌入一个过滤代理。邮件在被 cleanup 解析(包括地址重写、头部规范化)之前,先经由外部过滤器扫描。
# /etc/postfix/main.cf — before-queue proxy 配置
smtpd_proxy_filter = 127.0.0.1:10025
smtpd_proxy_options = speed-adjust
# 过滤器监听 10025 端口
# 注意: before-queue proxy 模式下,过滤代理必须返回与原始邮件相同的
# MAIL FROM 和 RCPT TO,否则 cleanup 会拒绝入队
此模式在 Postfix 2.x 中较为常见,但 3.x 以后逐渐被 milter 替代,因为 milter 更灵活且资源消耗更低。
四、After-queue(content_filter)过滤器
4.1 架构原理
content_filter 是 Postfix 中最成熟的内容过滤架构。配置后,Postfix 不直接将邮件投递到目标 MX,而是先投递到指定过滤服务(通常是一个监听端口的 Amavis/LMTP),过滤服务完成扫描后通过 SMTP/LMTP 再回流到 Postfix。
数据流:
SMTP 客户端 → Postfix smtpd → cleanup → queue → qmgr → [ content_filter:127.0.0.1:10024 ] → Amavis (+ ClamAV/Rspamd) → [localhost:10025] → Postfix smtpd → cleanup → queue → qmgr → MX
注意邮件被 Queue→Dequeue→Re-queue→再次 Dequeue,即所谓"二次入队"(double queueing)。这是 after-queue 最重要的延迟成本。
4.2 content_filter 配置
# /etc/postfix/main.cf
# 启用 content_filter 到 Amavis
content_filter = smtp-amavis:[127.0.0.1]:10024
# /etc/postfix/master.cf — Amavis 回流通道
smtp-amavis unix - - y - 2 smtp
-o smtp_data_done_timeout=1200
-o smtp_send_xforward_command=yes
127.0.0.1:10025 inet n - y - - smtpd
-o content_filter=
-o local_recipient_maps=
-o relay_recipient_maps=
-o smtpd_restriction_classes=
-o smtpd_delay_reject=no
-o smtpd_client_restrictions=permit_mynetworks,reject
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o mynetworks_style=host
-o smtpd_authorized_xforward_hosts=127.0.0.0/8
4.3 延迟影响分析
| 阶段 | 延迟来源 | 典型耗时 |
|---|---|---|
| 输出队列 | qmgr 调度到 Amavis | 0.05–0.5 s |
| Amavis 扫描 | 病毒库/垃圾规则匹配 | 0.1–10 s |
| 输入队列 | 回流到 smtpd (10025) | 0.05–0.3 s |
| 第二次 cleanup | 头部重写、邮件归档 | 0.05–0.2 s |
| 总附加延迟 | 0.3–11 s |
五、Pre-queue(SMP)过滤器
5.1 SMP 架构
SMP(Single Message Processing)与 after-queue 类似,但不经过 Postfix 的完整队列系统——邮件在 cleanup 阶段直接被转发到过滤服务,过滤结果直接决定是否入队。
# /etc/postfix/main.cf — pre-queue 近似实现
# (通过 content_filter + 自定义 master.cf 管道)
# 注意: 严格意义上 Postfix 中没有 "pre-queue filter" 的独立配置指令
# 但可以通过非队列通道避免二次入队
# master.cf:
filter unix - n n - 10 lmtp
-o lmtp_data_done_timeout=1200
-o lmtp_send_xforward_command=yes
-o disable_dns_lookups=yes
# main.cf:
content_filter = lmtp:127.0.0.1:10024
Postfix 社区将 pre-queue 定义为:在 cleanup 之后、qmgr 入队之前插入过滤步骤。真正的 pre-queue 只在 Postfix 3.0+ 的 experimental pre_queue_filter 中可用,且未正式进入生产推荐。
六、四维对比矩阵
| 维度 | Milter | Before-queue | After-queue | Pre-queue |
|---|---|---|---|---|
| 队列 I/O | 1次入队 | 1次入队 | 2次入队 | 1次入队 |
| SMTP 响应影响 | 阻塞(DATA 后等待) | 阻塞 | 立即 250 OK | 立即 250 OK |
| 修改邮件体 | 支持 | 支持 | 支持 | 有限 |
| SMTP 阶段拒绝 | 支持 | 支持 | 仅 DSN bounce | 仅 DSN bounce |
| 故障隔离 | 高(独立进程) | 中(依赖转发) | 高(独立进程) | 低(smtpd 内) |
| 资源消耗(smptd) | 低 | 中 | 低 | 高(smtpd 进程膨胀) |
| 对端备支持 | 一般(1:1 协议) | 好 | 好 | 差 |
| 典型延迟 (P50) | +0.5–3s | +0.1–1s | +0.3–11s | +0.1–0.5s |
| Postfix 版本兼容 | ≥ 2.3 | ≥ 2.0 | ≥ 2.0 | ≥ 3.0 (实验) |
| 生态成熟度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ |
七、混合部署案例
7.1 推荐混合架构
生产环境建议将 Milter 与 After-queue 结合使用:
SMTP 入站层:
smtpd → Rspamd milter (反垃圾/反病毒) → OpenDKIM milter (DKIM 签名/验证)
│ ├── Milter 在此阶段执行快速检查(IP 信誉、SPF/DKIM/DMARC 验证)
│ └── 拒绝结果直接在 SMTP 会话中返回 (550 5.7.1)
│
Postfix cleanup → queue → qmgr
│
content_filter → Amavis + ClamAV (深度病毒扫描)
│ └── 放置在此阶段的理由:不影响 SMTP 会话延迟
│
Postfix → queue → qmgr → MX 投递
# 对应配置
# main.cf
smtpd_milters = unix:/run/rspamd/rspamd-milter.sock
unix:/run/opendkim/opendkim.sock
content_filter = smtp-amavis:[127.0.0.1]:10024
# After-queue 配置说明:
# Rspamd milter 先做第一道过滤(10~200ms 内完成)
# 通过后的邮件经 Amavis + ClamAV 做深度扫描(1~10s)
# 这样 90% 的正常邮件快速通过,仅可疑邮件产生延迟
# Milter 拒绝日志
grep 'milter-reject' /var/log/mail.log | head -5
# Jul 24 10:15:33 mx1 postfix/smtpd[12345]: milter-reject: RCPT from ...
7.2 性能基准对比
以下数据基于 4 vCPU / 8 GB RAM 测试环境,附件 500 KB 邮件负载:
| 架构 | 吞吐(msg/min) | P50 延迟 | P99 延迟 | CPU 使用 |
|---|---|---|---|---|
| 无过滤(基线) | 12,000 | 0.2 s | 1.2 s | 15% |
| 仅 Milter (Rspamd) | 9,500 | 0.8 s | 3.5 s | 35% |
| 仅 After-queue (Amavis+ClamAV) | 2,800 | 3.2 s | 18 s | 60% |
| Milter + After-queue | 2,400 | 4.1 s | 22 s | 75% |
八、选型决策树
邮件的内容过滤是拦截型还是仅标记型?
├── 拦截型(需要 SMTP 阶段拒绝)
│ └── 使用 Milter (推荐 Rspamd/OpenDKIM)
│ └── 无法使用 Milter? → Before-queue proxy
│
└── 标记型(允许入站后标记,不拒绝)
└── 扫描速度要求 > 500 msg/s?
├── 是 → After-queue content_filter
└── 否 → 任何架构均可
延迟敏感度:
├── 高(SMTP 会话 ≤ 5s)→ Milter(快速检查层)
├── 中(接受 10s+)→ Milter + After-queue 混合
└── 低(投递后扫描)→ After-queue 仅
资源约束:
├── CPU/内存充足 → After-queue (Amavis + ClamAV + Rspamd)
├── CPU/内存有限 → Milter (Rspamd) 优先
└── 极低 → Pre-queue (但稳定性风险高)
总体建议:对于邮件安全网关场景,优先部署 Milter(Rspamd)做前置快速过滤 + After-queue content_filter(Amavis + ClamAV)做深度扫描的混合架构。Milter 捕获约 85%~95% 的可疑邮件并在 SMTP 阶段直接拒绝,避免队列 I/O 浪费;通过后的邮件经 after-queue 做全量病毒扫描,最大程度降低 end-to-end 延迟。如不需要深度病毒扫描,可仅使用 Milter,将 P50 延迟控制在 1 秒以内。
参考文献
- RFC 5321 — Simple Mail Transfer Protocol. IETF, October 2008. Section 4.5 (Timeouts).
- RFC 5322 — Internet Message Format. IETF, October 2008.
- RFC 1652 — SMTP Service Extension for 8bit-MIME Transport. IETF, July 1994.
- Sendmail, Inc. — Milter: Mail Filter API. https://www.milter.org/
- Postfix — FILTER_README: Content Filtering. https://www.postfix.org/FILTER_README.html
- Postfix — MILTER_README: Milter support. https://www.postfix.org/MILTER_README.html
- Rspamd — Milter Integration. https://rspamd.com/doc/arch.html
- Wietse Venema — Postfix Architecture. https://www.postfix.org/OVERVIEW.html
- M3AAWG — Email Filtering Architecture Best Practices (2024).
引用本文
ztpop.net 知识库编辑. "Postfix内容过滤器架构对比:milter、before-queue与after-queue" ztpop.net 知识库.
本站技术文章采用 CC-BY 4.0 许可,可自由引用,仅需标注来源 ztpop.net。
