简介
Postfix 实现了对 Sendmail 版本 8 Milter(邮件过滤器)协议的支持。该协议用于在 MTA 外部运行的应用程序,用于检查 SMTP 事件(CONNECT、DISCONNECT)、SMTP 命令(HELO、MAIL FROM 等)以及邮件内容(头部和正文)。所有这些操作都在邮件被放入队列之前进行。
在 Postfix 中添加 Milter 支持的原因是,存在大量应用程序,不仅用于阻止不需要的邮件,还用于验证真实性(示例: OpenDKIM 和 DMARC)或对邮件进行数字签名(示例:OpenDKIM)。为所有这些软件再开发一个 Postfix 专用的版本,是对人力和系统资源的低效利用。
Milter 协议随着时间的推移不断演进,不同版本的 Postfix 实现了不同的功能集。请参阅本文档末尾的 工作绕过方案 和 限制 部分,以了解 Postfix 和 Sendmail 实现之间的差异。
本文档提供了以下主题的信息:
- Milter 应用程序如何集成到 Postfix 中
- Postfix 和 Milter 在何时检查 SMTP 会话
- 构建 Milter 应用程序
- 运行 Milter 应用程序
- 配置 Postfix
- 解决方法
- 限制
Milter 应用程序如何集成到 Postfix 中
Postfix 的 Milter 实现使用了两组不同的邮件过滤器列表:一组仅用于 SMTP 邮件的过滤器列表,另一组用于非 SMTP 邮件的过滤器列表。这两组列表具有不同的功能,这点令人遗憾。要避免这种情况,需要对 Postfix 进行重大重构。
- 仅用于 SMTP 的过滤器处理通过 Postfix smtpd(8) 服务器接收的邮件。它们通常用于过滤不需要的邮件以及为授权的 SMTP 客户端签名邮件。您可以在后续章节中描述的 smtpd_milters 参数中指定 SMTP 专用 Milter 应用程序。通过 Postfix smtpd(8) 服务器到达的邮件不会被接下来描述的非 SMTP 过滤器过滤。
- 非 SMTP 过滤器处理通过 Postfix sendmail(1) 命令行或通过 Postfix qmqpd(8) 服务器到达的邮件。它们通常仅用于对邮件进行数字签名。虽然非 SMTP 过滤器可用于过滤不需要的邮件,但与仅限 SMTP 的过滤器相比,它们存在一定限制。您可通过 non_smtpd_milters 参数指定非 SMTP Milter 应用程序,具体方法将在后续章节中详细说明。
对于熟悉 Postfix 架构的用户,下图展示了 Milter 应用程序如何集成到 Postfix 中。带数字的名称是 Postfix 命令或服务器程序,而阴影区域内的未编号名称代表 Postfix 队列。为了避免混乱,本地提交的路径已被简化(OVERVIEW 文档对 Postfix 架构有更完整的描述)。
SMTP-only
过滤器非SMTP
过滤器
^
||
v
^
|
|
||
|
|
v网络 -> smtpd(8) \ 网络 -> qmqpd(8) -> cleanup(8) -> incoming / pickup(8) : 本地 -> sendmail(1)
当 Postfix 和 Milter 检查 SMTP 会话时
通常,Postfix 首先检查信息,然后是第一个配置的 Milter,第二个配置的 Milter,依此类推。
- 对于大多数 SMTP 命令:Postfix 检查一个 SMTP 命令,如果 Postfix 没有拒绝该命令,则将该命令传递给第一个配置的 Milter。如果第一个 Milter 没有拒绝该命令,Postfix 将其传递给第二个配置的 Milter,依此类推。这包括带有信封发送者(MAIL FROM)或信封接收者(RCPT TO)的命令。Postfix 会将相同的信封记录存储在队列文件中,与未配置 Milter 时相同,包括重写的信封地址、展开的虚拟别名、来自 sender/recipient_bcc_maps 的 BCC 地址等。
- 包含头部/正文内容:Postfix 可能在将内容存储到队列文件前重写或拒绝头部/正文内容;Postfix 存储的头部/正文内容与未配置 Milter 时相同。如果 Postfix 未拒绝头部/正文内容,则将其传递给第一个配置的 Milter,该 Milter 可能修改或拒绝该内容,或修改已存储的信封。如果第一个 Milter 未拒绝头部/正文内容,Postfix 将其传递给第二个配置的 Milter,依此类推。
详细信息:
- Postfix 隐藏其自身添加的 Postfix 前缀 Received: 头部,以兼容 Sendmail。Postfix 不隐藏 Postfix 或 Milters 添加或修改的其他头部。
- 当 Postfix SMTP 服务器收到一个或多个有效的 BDAT 命令序列时,它会为 Milter 生成一个 DATA 命令。
Milter API 不支持检查 QUIT、NOOP 或 VRFY 等 SMTP 命令;API 仅支持与电子邮件投递相关的命令。
构建 Milter 应用程序
Milter 应用程序已用 C、Haskell、Java、Perl、Python、Rust 等语言编写,但本文档仅涵盖 C 应用程序。对于这些应用程序,您需要一个实现 Sendmail 8 Milter 协议的对象库。Postfix 目前不提供此类库,但 Sendmail 提供。
部分系统默认安装 Sendmail libmilter 库。在其他系统中,libmilter 可能通过包(在某些 Linux 系统中称为 "sendmail-devel")提供。
安装 libmilter 后,应用程序如 OpenDKIM 和 OpenDMARC 可直接编译使用,无需任何额外配置:
$ gzcat opendkim-x.y.z.tar.gz | tar xf - $ cd opendkim-x.y.z $ ./configure ...options... $ make [...大量输出省略...] $ make install
运行 Milter 应用程序
要运行 Milter 应用程序,请参阅过滤器的文档以获取选项。一个典型的命令如下:
# /some/where/opendkim -l -u userid -p inet:portnumber@localhost ...other options...
请指定一个未在其他应用程序中使用的 userid 值(例如,不要使用 "postfix" 或 "www" 等)。
配置 Postfix
与 Sendmail 类似,Postfix 提供了大量配置选项,用于控制其与 Milter 应用程序的通信方式。除了适用于所有 Milter 应用程序的全局选项外,Postfix 3.0 及更高版本还支持按 Milter 设置超时、按 Milter 处理错误等功能。
本节包含以下内容:
- 仅 SMTP Milter 应用程序
- 非 SMTP Milter 应用程序
- Milter 错误处理
- Milter 协议版本
- Milter 协议超时设置
- 不同 Milter 应用程序的设置
- 不同 SMTP 客户端的设置
- Sendmail 宏模拟
- Postfix 会向 Milter 发送哪些宏?
仅 SMTP Milter 应用程序
SMTP 专用 Milter 应用程序处理通过 Postfix smtpd(8) 服务器到达的邮件。它们通常用于过滤不需要的邮件,并为授权的 SMTP 客户端签名邮件。通过 Postfix smtpd(8) 服务器接收的邮件不会被下一节中描述的非 SMTP 过滤器进行过滤。
注意:对于 Postfix 版本中 mail_release_date 之前:请勿使用 header_checks(5) 的 IGNORE 操作来移除 Postfix 自身的 Received: 消息头。这会导致邮件签名过滤器出现问题。相反,应保留 Postfix 自身的 Received: 消息头,并使用 header_checks(5) REPLACE 操作来清理信息。
您使用smtpd_milters参数指定仅用于SMTP的Milter应用程序(可以有多个)。每个Milter应用程序通过其监听套接字的名称进行标识;其他Milter配置选项将在后续章节中讨论。Postfix 根据 smtpd_milters 中配置的顺序向每个 Milter 应用程序发送命令。当一个 Milter 应用程序拒绝一个命令时,这将覆盖其他 Milter 应用程序的响应。
/etc/postfix/main.cf: # 通过 smtpd(8) 服务器接收的邮件的 Milter。 # 参见下方关于套接字地址的语法。 smtpd_milters = inet:localhost:portnumber ...other filters...
监听套接字的一般语法如下:
unix:路径名
连接到绑定到指定路径名的本地 UNIX 域服务器。如果 smtpd(8) 或 cleanup(8) 进程以 chroot 方式运行,则绝对路径名将相对于 Postfix 队列目录进行解释。在许多系统中,local 是 unix 的同义词。
inet:主机:端口
连接到指定本地或远程主机上的指定 TCP 端口。主机和端口可以以数字或符号形式指定。
注意:Postfix 语法与 Milter 语法不同,后者采用以下形式:inet:port@host。
有关高级配置,请参阅"不同 SMTP 客户端的不同设置"和"不同 Milter 应用程序的不同设置"。
非 SMTP Milter 应用程序
非 SMTP Milter 应用程序处理通过 Postfix sendmail(1) 命令行或通过 Postfix qmqpd(8) 服务器到达的邮件。它们通常用于对邮件进行数字签名。虽然非 SMTP 过滤器可用于过滤不需要的邮件,但如本节后文所述,此类过滤器存在一定限制。通过 Postfix smtpd(8) 服务器接收的邮件不会被非 SMTP 过滤器过滤。
注意:请勿使用 header_checks(5) 的 IGNORE 操作来移除 Postfix 自身的 Received: 消息头。这会导致邮件签名过滤器出现问题。相反,应保留 Postfix 自身的 Received: 消息头,并使用 header_checks(5) REPLACE 操作来清理信息。
您使用 non_smtpd_milters 参数指定非 SMTP Milter 应用程序。该参数与上一节中的smtpd_milters参数使用相同的语法。与仅限 SMTP 的过滤器类似,您可以指定多个 Milter 应用程序。Postfix 會按照 non_smtpd_milters 中配置的順序,依序向每個 Milter 應用程式傳送指令。當某個 Milter 應用程式拒絕指令時,該拒絕會覆蓋其他 Milter 應用程式的回應。
/etc/postfix/main.cf: # 非 SMTP 邮件的 Milter。 # 参见下方关于套接字地址的语法。 non_smtpd_milters = inet:localhost:portnumber ...other filters...
在使用 Milter 应用程序处理非 SMTP 邮件时,有一个小问题:不存在 SMTP 会话。为了让 Milter 应用程序正常工作,Postfix 的 cleanup(8) 服务器实际上需要模拟 SMTP 客户端的 CONNECT 和 DISCONNECT 事件,以及 SMTP 客户端的 EHLO、MAIL FROM、RCPT TO 和 DATA 命令。
- 当通过 sendmail(1) 命令行接收新邮件时,Postfix cleanup(8) 服务器会假装邮件通过 ESMTP 从 "localhost"(IP 地址为 "127.0.0.1")发送。结果与 Sendmail 8.12 及更高版本中命令行提交时的行为非常相似,尽管 Sendmail 采用不同的机制实现此结果。
- 当新邮件通过 qmqpd(8) 服务器到达时,Postfix cleanup(8) 服务器会假装邮件是通过 ESMTP 到达的,并使用 QMQPD 客户端的主机名和 IP 地址。
- 当旧邮件通过 "postsuper -r" 重新注入队列时,Postfix cleanup(8) 服务器会使用与邮件首次到达时相同的客户端信息。
这通常按预期工作,只有一个例外:非 SMTP 过滤器不得拒绝或临时拒绝模拟的 RCPT TO 命令。当一个 non_smtpd_milters 应用程序 REJECT 或 TEMPFAIL 某个收件人时,Postfix 会报告配置错误,邮件将留在队列中。
签名内部生成的退信消息
Postfix 通常不会对内部生成的邮件(如退信或邮局管理员通知)应用内容过滤器。过滤内部生成的退信会导致邮件丢失,因为当过滤器拒绝一条消息时,生成的双退信消息几乎肯定也会被阻止。
要为 Postfix 的退信消息签名,请启用对内部生成的退信消息的过滤(下文第 2 行),并不要拒绝任何内部生成的退信消息,使用 non_smtpd_milters、header_checks 或 body_checks(下文第 3-5 行)。
1 /etc/postfix/main.cf: 2 internal_mail_filter_classes = bounce 3 non_smtpd_milters = 不拒绝内部生成的退信 4 header_checks = 不拒绝内部生成的退信 5 body_checks = 不拒绝内部生成的退信
Milter 错误处理
milter_default_action 参数指定 Postfix 如何处理 Milter 应用程序错误。默认操作是返回临时错误状态,以便客户端稍后重试。如果希望将邮件视为过滤器不存在并接收邮件,请指定 "accept";如果希望以永久状态拒绝邮件,请指定 "reject"。"quarantine" 操作与 "accept" 类似,但会将消息冻结在 "hold" 队列中,且仅在 Postfix 2.6 及更高版本中可用。
/etc/postfix/main.cf: # 出现错误时如何处理?指定 accept、reject、tempfail, # 或 quarantine(Postfix 2.6 或更高版本)。 milter_default_action = tempfail
参见"不同 Milter 应用程序的设置"以获取高级配置选项。
Milter 协议版本
由于 Postfix 未与 Sendmail libmilter 库一起编译,您可能需要配置 Postfix 应使用的 Milter 协议版本。默认版本为 6(Postfix 2.6 之前默认版本为 2)。
/etc/postfix/main.cf: # Postfix ≥ 2.6 milter_protocol = 6 # Postfix 版本在 2.3 到 2.5 之间 milter_protocol = 2
如果 Postfix 的 milter_protocol 设置指定的版本过低,libmilter 库将记录一条错误消息,例如:
应用程序名称: st_optionneg[xxxxx]: 0xyy 不满足操作要求 0xzz
解决方法是增加 Postfix 的 milter_protocol 版本号。不过,请参阅下文的 限制 部分,了解 Postfix 不支持的特性。
在 Postfix 2.7 及更早版本中,如果 Postfix 的 milter_protocol 设置指定的版本过高,libmilter 库会直接挂起而不会记录警告,您会看到类似以下的 Postfix 警告消息:
警告:milter inet:主机:端口: 无法读取数据包头:未知错误:0 警告:milter inet:主机:端口: 无法读取数据包头:成功 警告:milter inet:主机:端口: 无法读取 SMFIC_DATA 响应数据包头:没有此文件或目录
解决方法是降低 Postfix 的 milter_protocol 协议版本号。Postfix 2.8 及更高版本会自动禁用应用程序的 libmilter 库不支持的协议功能。
请参阅"不同 Milter 应用程序的不同设置"以获取高级配置选项。
Milter 协议超时
Postfix 在不同的 Milter 协议阶段使用不同的时间限制。下表显示了超时设置及其对应的协议阶段(EOH = 头部结束;EOM = 消息结束)。
Postfix 参数 时间限制 Milter 协议阶段 milter_connect_timeout 30s CONNECT milter_command_timeout 30s HELO, MAIL, RCPT, DATA, UNKNOWN milter_content_timeout 300s HEADER, EOH, BODY, EOM
注意:对于进行大量 DNS 查询的 Milter 应用程序,30 秒可能过短。然而,如果将上述超时时间设置过高,远程 SMTP 客户端可能会断开连接,且邮件可能被多次投递。这是队列前过滤的固有问题。
参见"不同 Milter 应用程序的设置"以获取高级配置选项。
不同 Milter 应用程序的设置
前面的部分列出了 Postfix main.cf 中的多个参数,用于控制所有 Postfix Milter 客户端的时间限制和其他设置。这对于简单配置已足够。在更复杂的配置中,为不同 Milter 客户端设置不同参数变得必要。此功能在 Postfix 3.0 及更高版本中得到支持。
以下示例展示了一个"非关键"Milter 客户端,其连接超时时间较短,且当服务不可用时默认操作为"接受"。
1 /etc/postfix/main.cf: 2 smtpd_milters = { inet:host:port, 3 connect_timeout=10s, default_action=accept }
现在,我们不再使用服务器端点,而是使用一个用 {} 括起来的列表。
- 第 2 行:列表中的第一个项是服务器端点。它支持与之前描述的完全相同的 "inet" 和 "unix" 语法。
- 第 3 行:列表的其余部分包含每个 Milter 的设置。这些设置会覆盖全局 main.cf 参数,且与这些参数具有相同名称,但不带 "milter_" 前缀。Postfix 3.0 及更高版本支持的 Milter 特定设置包括 command_timeout、connect_timeout、content_timeout、default_action 和 protocol。
在列表中,语法与我们从 main.cf 中已知的语法类似:项目用空格或逗号分隔。有一个区别:如果你希望在值内部或"="周围包含空格或逗号,必须将设置用大括号括起来,格式为"{ name = value }"。
为不同 SMTP 客户端设置不同参数
smtpd_milter_maps 功能支持为不同客户端 IP 地址设置不同的 Milter 参数。查找结果将覆盖全局 smtpd_milters 设置,且具有相同的语法。例如,要禁用本地地址范围的 Milter 设置:
/etc/postfix/main.cf: smtpd_milter_maps = cidr:/etc/postfix/smtpd_milter_map smtpd_milters = inet:host:port, { inet:host:port, ... }, ... /etc/postfix/smtpd_milter_map: # 禁用本地客户端的 Milters。 127.0.0.0/8 DISABLE 192.168.0.0/16 DISABLE ::/64 DISABLE 2001:db8::/32 DISABLE
此功能在 Postfix 3.2 及更高版本中可用。
Sendmail 宏模拟
Postfix 模拟了 Sendmail 的部分宏,具体如下表所示。部分宏的值取决于收件人是否被拒绝(被拒绝的收件人信息可由 Milter 应用程序请求获取)。不同 Milter 协议阶段(EOH = 头部结束,EOM = 消息结束)可用的宏不同;其可用性与 Sendmail 中并不完全一致。请参阅下文的工作绕过方法部分以获取解决方案。
HELO, MAIL, DATA, EOH, EOM
Sendmail 宏 Milter 协议阶段 描述 i DATA, EOH, EOM 队列 ID,也是 Postfix 队列文件名 j 始终 myhostname 的值 _ 始终 验证后的客户端名称和地址 {auth_authen} MAIL, DATA, EOH, EOM SASL 登录名称 {auth_author} MAIL, DATA, EOH, EOM SASL 发送者 {auth_type} MAIL, DATA, EOH, EOM SASL 登录方法 {client_addr} 始终 远程客户端 IP 地址 {client_connections} CONNECT 此客户端的连接并发数(如果客户端被排除在所有 smtpd_client_* 限制之外,则为 0)。 {client_name} 始终 远程客户端主机名
当地址到名称解析或名称到地址验证失败时: "未知"{client_port} 始终(Postfix ≥2.5) 远程客户端TCP端口 {client_ptr} CONNECT, HELO, MAIL, DATA 从地址到名称解析获取的客户端名称
当地址到名称解析失败时: "unknown"{证书颁发者} HELO, MAIL, DATA, EOH, EOM TLS 客户端证书颁发者 {证书主题} HELO, MAIL, DATA, EOH, EOM TLS 客户端证书主题 {cert_subject}HELO, MAIL, DATA, EOH, EOM TLS 密码套件 {daemon_addr} 始终(Postfix ≥3.2) 本地服务器 IP 地址 {daemon_name} 始终 milter_macro_daemon_name 的值 {daemon_port} 始终(Postfix ≥3.2) 本地服务器 TCP 端口 {mail_addr} 发件人地址 {mail_host} MAIL(Postfix ≥ 2.6,仅与smtpd_milters配合使用) 发件人下一跳目标 {mail_mailer} MAIL(Postfix ≥ 2.6,仅与smtpd_milters配合使用) 发件人邮件投递传输方式 {rcpt_addr} RCPT 收件人地址
若收件人被拒绝:描述性文本{rcpt_host} RCPT(Postfix ≥ 2.6,仅与smtpd_milters配合使用) 收件人下一跳目的地
当收件人被拒绝时:增强状态代码{rcpt_mailer} RCPT(Postfix ≥ 2.6,仅与smtpd_milters配合使用) 收件人邮件投递传输
收件人被拒绝:"错误"{tls_version} HELO, MAIL, DATA, EOH, EOM TLS协议版本 v 始终 milter_macro_v 的值
Postfix 将向 Milters 发送哪些宏?
Postfix 在不同的 Milter 协议阶段发送特定的宏集。这些宏的名称通过下表中所示的参数进行配置(EOH = 头部结束;EOM = 消息结束)。某些列表需要最低的 Milter 协议版本。
从 Sendmail 8.14.0 开始,Milter 应用程序可以指定在不同 Milter 协议阶段希望接收的宏。应用程序指定的列表优先于 Postfix 指定的列表。
Postfix 参数 Milter 协议版本 Milter协议阶段 milter_connect_macros 2或更高 连接 milter_helo_macros 2 或更高版本 HELO/EHLO milter_mail_macros 2 或更高版本 MAIL FROM milter_rcpt_macros 2 或更高版本 RCPT TO milter_data_macros 4 或更高版本 DATA milter_end_of_header_macros 6 或更高版本 EOH milter_end_of_data_macros 2 或更高版本 EOM milter_unknown_command_macros 3 或更高版本 未知命令
默认情况下,Postfix 只会发送那些值已通过 main.cf 或 master.cf 中更新过值的宏(例如:SMTP 会话(例如 SASL 登录或 TLS 证书)或邮件投递交易(例如队列 ID、发件人或收件人))。
要强制发送宏,即使其值未更新,您可以使用 milter_macro_defaults 参数指定宏的默认值。指定零个或多个以逗号或空格分隔的name=value对;您甚至可以指定Postfix不认识的宏名称!
解决方法
为了避免 SMTP 基于内容过滤器破坏 DKIM 等签名,请在 master.cf 中更新 before-filter SMTP 客户端,并添加一行 "-o disable_mime_output_conversion=yes"(注意:等号两侧无空格)。详细信息请参阅高级内容过滤器示例。
/etc/postfix/master.cf: # ============================================================= # 服务类型 权限 chroot 唤醒 最大进程数 命令 # (是) (是) (是) (从不) (100) # ============================================================= scan unix - - n - 10 smtp -o smtp_send_xforward_command=yes -o disable_mime_output_conversion=yes -o smtp_generic_maps=
- 某些 Milter 应用程序使用 "{if_addr}" 宏来识别本地邮件;该宏在 Postfix 中不存在。解决方法:使用 "{daemon_addr}"(Postfix ≥ 3.2)或 "{client_addr}" 宏代替。
某些 Milter 应用程序会记录类似以下的警告:
sid-filter[36540]: WARNING: sendmail 符号 'i' 不存在
并且可能插入包含 "unknown-msgid" 的丑陋消息头,例如:
X-SenderID: Sendmail Sender-ID Filter vx.y.z host.example.com <unknown-msgid>
问题在于,Milter 应用程序期望在 MTA 接受 MAIL FROM(发件人)命令之前,队列 ID 已知。Postfix 不会在接受第一个有效的 RCPT TO(收件人)命令之前选择队列 ID,该 ID 将用作队列文件名。
如果你遇到丑陋的头部问题,请检查 Milter 应用程序的最新版本是否已修复此问题。例如,当前版本的 dkim-filter 和 dk-filter 已经包含在协议的后期阶段查找 Postfix 队列 ID 的代码,而 sid-filter 版本 1.0.0 不再在消息头中包含队列 ID。
要修复丑陋的邮件头,您需要在某个较晚的时点添加代码以查找 Postfix 队列 ID。下面的示例在消息结尾后添加了查找逻辑。
- 编辑过滤器源文件(通常命名为 xxx-filter/xxx-filter.c 或类似名称)。
- 查找 mlfi_eom() 函数,并在顶部附近添加以下以 粗体 显示的代码:
dfc = cc->cctx_msg; assert(dfc != NULL); /* 确定用于日志记录的作业 ID。 */ if (dfc->mctx_jobid == 0 || strcmp(dfc->mctx_jobid, JOBIDUNKNOWN) == 0) { char *jobid = smfi_getsymval(ctx, "i"); if (jobid != 0) dfc->mctx_jobid = jobid; }备注:
- 不同的邮件过滤器对变量使用略微不同的名称。如果上述代码无法编译,请在邮件过滤器的源文件中查找查找"i"宏值的代码,并复制该代码。
- 此更改仅修复了丑陋的邮件头,但未修复 WARNING 消息。幸运的是,许多 Milters 仅记录该消息一次。
限制
本节列出了 Postfix Milter 实现的限制。随着实现的扩展,部分限制将被移除。当然,内容过滤的常规限制始终适用。请参阅 CONTENT_INSPECTION_README 文档以获取详细讨论。
Milter 协议随时间演进。因此,不同版本的 Postfix 实现支持的功能集不同。
Postfix 支持的 Milter 请求 2.6 Sendmail 8.14.0 中的所有 Milter 请求(见下文注释)。 2.5 Sendmail 8.14.0 中的所有 Milter 请求,除以下情况外:
SMFIP_RCPT_REJ(将被拒绝的收件人报告给邮件过滤器),
SMFIR_CHGFROM(替换发件人,可选 ESMTP 参数),
SMFIR_ADDRCPT_PAR(添加收件人,可选 ESMTP 参数)。2.4 Sendmail 8.13.0 的所有 Milter 请求。 2.3 Sendmail 8.13.0 的所有 Milter 请求,除以下情况外:
SMFIR_REPLBODY(替换消息正文)。- 对于用 C 语言编写的 Milter 应用程序,您需要使用 Sendmail libmilter 库。
Postfix 有两组邮件过滤器:仅用于 SMTP 邮件的过滤器(通过 smtpd_milters 参数指定),以及用于非 SMTP 邮件的过滤器(通过 non_smtpd_milters 参数指定)。非 SMTP 过滤器主要用于本地提交。
当邮件通过 non_smtpd_milters 过滤时,Postfix 的 cleanup(8) 服务器需要模拟 SMTP 客户端请求。这与预期一致,仅有一个例外: non_smtpd_milters 不得对模拟的 RCPT TO 命令返回 REJECT 或 TEMPFAIL 响应。当此规则被违反时,Postfix 将报告配置错误,邮件将留在队列中。
- 当您使用入站 SMTP 邮件的队列前内容过滤器(参见 SMTPD_PROXY_README)时,Milter 应用程序仅能访问 SMTP 命令信息;它们无法访问邮件头或正文,也无法对邮件或信封进行修改。
Postfix 3.3 及更高版本支持在替换信封发件人(SMFIR_CHGFROM)请求中使用 ESMTP 参数 RET 和 ENVID。当 Milter 应用程序提供其他 ESMTP 参数时,Postfix 会记录一条警告消息:
警告:队列 ID: cleanup_chg_from: 忽略无效的 ESMTP 参数 "whatever" 在 SMFI_CHGFROM 请求中
Postfix 3.0 及更高版本支持在请求添加信封收件人时使用 ESMTP 参数 NOTIFY 和 ORCPT。当 Milter 应用程序提供其他 ESMTP 参数时,Postfix 会记录一条警告消息:
警告: 队列 ID: cleanup_add_rcpt: 忽略来自 Milter 或 header/body_checks 的 ESMTP 参数 "whatever"
Postfix 2.6 及更高版本在请求替换发件人(SMFIR_CHGFROM)或附加收件人(SMFIR_ADDRCPT_PAR)时,会忽略可选的 ESMTP 参数。当 Milter 应用程序提供此类 ESMTP 参数时,Postfix 会记录一条警告消息:
警告: 队列 ID: cleanup_chg_from: 忽略 ESMTP 参数 "whatever" 警告: 队列 ID: cleanup_add_rcpt: 忽略 ESMTP 参数 "whatever"
Postfix 2.3 不支持替换消息正文的请求。当需要此不支持的操作时,Milter 应用程序会记录一条警告消息:
st_optionneg[134563840]: 0x3d 不满足操作要求 0x1e
解决方案是使用 Postfix 2.4 或更高版本。
- Postfix 3.0 之前的版本不支持按 Milter 设置超时、按 Milter 处理错误等功能。