跳转到主要内容

于 2025年04月22日 摘录自 Postfix Address Verification Howto

警告

收件人地址验证可能在字典攻击或大量回弹邮件的情况下导致下游服务器负载增加。发件人地址验证可能导致您的网站被某些提供商列入黑名单。请参阅下文的"限制"部分以获取更多信息。

Postfix 地址验证能为您做些什么

地址验证是一项功能,允许 Postfix SMTP 服务器在地址被验证为可投递之前,阻止发件人(MAIL FROM)或收件人(RCPT TO)地址。

该技术可用于拒绝带有无法回复发件人地址的垃圾邮件。

该技术还可用于阻止无法投递的收件人邮件,例如在没有完整有效收件人地址列表的邮件中继主机上。这可防止无法投递的垃圾邮件进入队列,从而避免Postfix浪费资源尝试发送MAILER-DAEMON消息。

此功能在Postfix 2.1及更高版本中可用。

本文档涵盖的主题:

地址验证的工作原理

Postfix MTA 通过探测该地址的首选 MTA 来验证发件人或收件人地址,而不会实际投递邮件。首选 MTA 可能包括 Postfix MTA 本身,或一些远程 MTA(SMTP 中断)。探测消息与普通邮件类似,但它们从未被投递、延迟或退回;探测消息始终被丢弃。

 -> 探针
消息
-> 后缀
邮件
队列
 
Internet-> Postfix
SMTP
服务器
<-> Postfix
验证
服务器
 |
v
<- probe
status
<- Postfix
delivery
agents
-> 本地
-> 远程
 ^
|
v
 
 
 地址
验证
数据库

启用 Postfix 地址验证后,正常邮件在首次验证地址时仅会延迟最多 6 秒。一旦地址状态确定,该状态将被缓存,Postfix 立即回复。

当验证耗时过长时,Postfix SMTP 服务器将以 450 响应推迟发送者或接收者地址。普通邮件客户端将在一段时间后重新连接。地址验证延迟可通过 main.cf address_verify_poll_countaddress_verify_poll_delay 参数。详情请参阅 postconf(5)

地址验证的限制

  • Postfix 假设远程 SMTP 服务器会在 RCPT TO 命令的响应中拒绝未知地址。然而,部分站点会在 DATA 命令的响应中报告此情况。对于此类站点,您可以通过配置 smtp_address_verify_target 参数(Postfix 3.0 及更高版本)来设置绕过方案。
  • 在验证远程地址时,Postfix 会探测该地址的首选 MTA,但不会实际发送邮件。如果首选 MTA 接受该地址,则 Postfix 认为该地址可投递。实际上,发往远程地址的邮件可能在首选 MTA 接受收件人地址后,或在首选 MTA 接受邮件内容后被退回。
  • 部分站点可能在您频繁探测其服务器(探测指不发送邮件的 SMTP 会话)或频繁探测不存在的地址时将您加入黑名单。这是为何在站点接收大量邮件时应尽量避免使用发件人地址验证的原因之一。
  • 通常,地址验证探测消息与常规邮件遵循相同的路径。然而,部分站点通过中间relayhost将邮件转发至互联网,这会导致地址验证失效。请参阅下方"控制地址验证探测消息的路由"部分,了解如何覆盖邮件路由以及在必须这样做时可能存在的限制。
  • Postfix 假设当某个地址的首选 MTA 拒绝探测请求时,该地址不可投递,无论拒绝原因(客户端被拒、HELO 被拒、MAIL FROM 被拒等)。因此,当某个地址的首选 MTA 因任何原因拒绝来自您机器的邮件时,Postfix 都会拒绝该地址。这并非限制,但在此提及以防有人误认为是限制。
  • 遗憾的是,某些站点在响应 RCPT TO 或 DATA 命令时不会拒绝未知地址,而是在传输消息后,在 DATA 结束时报告投递失败。Postfix 地址验证无法与这类站点配合使用。
  • 默认情况下,Postfix 探测邮件的发件人地址为 "double-bounce@$myorigin"(Postfix 2.5 之前的版本默认是 "postmaster@$myorigin")。这是安全的,因为 Postfix SMTP 服务器不会拒绝发送到此地址的邮件。

    您可以将探测发送地址更改为空地址("address_verify_sender =")。这不安全,因为地址探测会失败于配置错误的站点(这些站点会拒绝 MAIL FROM: <>),而来自 "double-bounce@$myorigin" 的探测则会成功。

  • 使用非空发件人地址的缺点是,该地址可能被列入垃圾邮件发送者的邮件列表。尽管 Postfix 会始终丢弃发送到双重退信地址的邮件,但这仍会导致网络带宽和服务器资源的浪费。为了防止地址被收集,Postfix 2.9 及更高版本支持在指定非零 address_verify_sender_ttl 值时使用时间依赖的发件人地址。

收件人地址验证

如前所述,收件人地址验证有助于阻止邮件中继主机向无法投递的收件人发送邮件,尤其是当该主机没有所有有效收件人地址列表时。这可以防止邮件队列被MAILER-DAEMON消息填满。

收件人地址验证相对简单,没有意外情况。如果收件人探测失败,Postfix 将拒绝发往该收件人地址的邮件。如果收件人探测成功,Postfix 将接受发往该收件人地址的邮件。然而,当系统被大量回弹邮件淹没,或某些垃圾邮件发送者发起字典攻击时,收件人地址验证探测可能会增加下游 MTA 的负载。

默认情况下,地址验证结果保存在一个持久化数据库中(Postfix 2.7 及更高版本;较早版本需在main.cf中指定数据库,具体方法见后文)。持久化数据库可避免对同一地址重复探测。

/etc/postfix/main.cfsmtpd_recipient_restrictions = 
permit_mynetworks
# reject_unauth_destination 在此处无需设置,如果邮件
# 在 smtpd_relay_restrictions 中指定了中继策略
# (Postfix 2.10 及更高版本可用)。
reject_unauth_destination
...
reject_unknown_recipient_domain
reject_unverified_recipient
...
# Postfix 2.6 及更高版本的隐私功能。
# unverified_recipient_reject_reason = 地址解析失败
# Postfix 3.2 及更早版本的临时解决方案。
# 不要设置 enable_original_recipient=no。这将防止 Postfix
# 在原始地址下保存收件人地址验证结果
# 当地址验证探针
# 消息通过地址别名或规范映射时。

"reject_unknown_recipient_domain"限制会阻止发往不存在域的邮件。将此限制放在 "reject_unverified_recipient" 之前,可避免生成不必要的探测消息。

unverified_recipient_reject_code 参数(默认值为 450)指定当收件人地址已知无法投递时,Postfix SMTP 服务器返回的数值代码。当您信任 Postfix 的判断时,将此设置改为 550。

以下功能在 Postfix 2.6 及更高版本中可用。

unverified_recipient_defer_code 参数(默认值为 450)指定当收件人地址探测因临时错误失败时,Postfix SMTP 服务器返回的数值代码。部分站点坚持将此值修改为 250。注意:此更改会在负载较高时使 MX 服务器成为反向散射源。

unverified_recipient_reject_reason 参数(默认值为空)指定 Postfix 发送给远程 SMTP 客户端的固定文本,而非实际的地址验证详细信息。不要指定 SMTP 状态代码或增强状态代码。

unverified_recipient_tempfail_action 参数(默认值: defer_if_permit)指定 Postfix SMTP 服务器在收件人地址验证探测因临时错误失败时的操作。

来自频繁被伪造域名的邮件发件人地址验证

仅适用于非常小的站点,对于经常出现在伪造邮件中的特定域,启用发件人地址验证相对安全。

/etc/postfix/main.cfsmtpd_sender_restrictions = hash:/etc/postfix/sender_access
unverified_sender_reject_code = 550
# Postfix 2.6 及更高版本。
# unverified_sender_defer_code = 250
# Postfix 2.7 及更高版本的默认设置。
# 注释 1:请务必阅读下方的"缓存"部分!
# 注释 2:避免在此处使用哈希文件。建议使用 btree 或 lmdb 替代。
address_verify_map = btree:/var/lib/postfix/verify
# Postfix 3.2 及更早版本的解决方法。
# 不要设置 enable_original_recipient=no。这会防止 Postfix
# 将发件人地址验证结果保存到
# 原地址下,当地址验证探测消息
# 通过地址别名或规范映射时,Postfix 不会将发件人地址验证结果保存到 # 原地址下。

/etc/postfix/sender_access:
# 当处理大量邮件时,请勿执行此操作。
aol.com reject_unverified_sender
hotmail.com reject_unverified_sender
bigfoot.com reject_unverified_sender...
 等等 ...

在网络空间/时间的某个时刻,可以在http://www.monkeys.com/anti-spam/filtering/sender-domain-validate.in中找到一个经常被伪造的MAIL FROM域名的列表。

注意:您可能首先需要为所有自有域名启用发件人地址验证。

为所有邮件启用发件人地址验证

遗憾的是,无法简单地为所有邮件启发件地址验证——您可能会丢失来自配置错误系统的合法邮件。您几乎肯定需要为特定地址或整个域设置允许列表。

要了解发件人地址验证对您的邮件有何影响,请指定 "warn_if_reject reject_unverified_sender",以便查看会被阻止的邮件:

/etc/postfix/main.cfsmtpd_sender_restrictions = 
permit_mynetworks
... 
check_sender_access hash:/etc/postfix/sender_access
reject_unknown_sender_domain
warn_if_reject reject_unverified_sender 
...
# Postfix 2.6 及更高版本。
# unverified_sender_reject_reason = 发件人地址验证失败
# Postfix 2.7 及更高版本的默认设置。
# 注意 1:请务必阅读下方的"缓存"部分!
# 注意 2:避免在此处使用哈希文件。建议使用 btree 或 lmdb 替代。
address_verify_map = btree:/var/lib/postfix/verify

这也是在实际拒绝邮件之前,用地址验证结果填充缓存的好方法。

sender_access 限制用于将已知安全的域或地址加入白名单。尽管 Postfix 在探测失败后不会将已知安全的地址标记为不良,但谨慎起见还是建议这样做。

注意:您需要将 securityfocus.com 等网站加入白名单,这些网站运营的邮件列表会为每条帖子使用不同的发件人地址(VERP)。此类地址会迅速污染地址验证缓存,并触发不必要的发件人验证探测。

/etc/postfix/sender_access
securityfocus.com OK
...

"reject_unknown_sender_domain"限制会阻止来自不存在域的邮件。将此设置放在 "reject_unverified_sender" 之前,可避免生成不必要的探测消息。

unverified_sender_reject_code 参数(默认值为 450)指定当发件人地址已知为无法投递时,Postfix 服务器返回的数值代码。当您信任 Postfix 的判断时,将此设置改为 550。

以下功能在 Postfix 2.6 及更高版本中可用。

unverified_sender_defer_code 参数(默认值为 450)指定当发件人地址验证探测因临时错误失败时,Postfix SMTP 服务器返回的数值代码。请指定有效的 2xx 或 4xx 代码。

unverified_sender_reject_reason 参数(默认值为空)指定 Postfix 将发送给远程 SMTP 客户端的固定文本,而非实际的地址验证详细信息。不要指定 SMTP 状态代码或增强状态代码。

unverified_sender_tempfail_action 参数(默认值: defer_if_permit)指定 Postfix SMTP 服务器在发件人地址验证探测因临时错误失败时的操作。

地址验证数据库

为了提升性能,Postfix verify(8) 守护进程可将地址验证结果保存至持久化数据库。此功能在 Postfix 2.7 及后续版本中默认启用。address_verify_map(注意:单数形式)配置参数指定用于存储发件人或收件人地址验证结果的持久化存储位置。如果指定空值,所有地址验证结果将在执行 "postfix reload" 或 "postfix stop" 后丢失。

# 示例 1:Postfix 2.7 及更高版本的默认设置。
# 注意:此处避免使用哈希文件。建议使用 btree 或 lmdb 替代。
/etc/postfix/main.cfaddress_verify_map = btree:$data_directory/verify_cache
# 示例 2:共享持久化 lmdb:缓存(Postfix 2.11 或更高版本)。
# 禁用所有 Postfix 实例中的自动缓存清理,
# 除一个负责缓存清理的实例外。
/etc/postfix/main.cfaddress_verify_map = lmdb:$data_directory/verify_cache
# 地址验证缓存清理间隔 = 0
# 示例 3:共享持久化 btree:缓存(Postfix 2.9 或更高版本)。
# 禁用所有 Postfix 实例中的自动缓存清理,
# 除一个负责缓存清理的实例外。
/etc/postfix/main.cfaddress_verify_map = proxybtree:$data_directory/verify_cache
# 地址验证缓存清理间隔 = 0
# 示例 4:共享内存缓存(需要 Postfix 2.9 或更高版本)。
# 禁用所有 Postfix 实例中的自动缓存清理。
# 详情请参阅 memcache_table(5)。
/etc/postfix/main.cfaddress_verify_map = memcache:/etc/postfix/verify-memcache.cf
address_verify_cache_cleanup_interval = 0
# 示例 5:Postfix 2.6 及更早版本的默认设置。
# 仅使用非持久化存储。
/etc/postfix/main.cfaddress_verify_map =

注意 1:数据库文件应存储在 Postfix 拥有的目录下,例如 $data_directory

从版本 2.5 开始,Postfix 在打开此文件时不再使用 root 权限。为了保持向后兼容性,如果尝试在非 Postfix 目录下打开该文件,系统会将其重定向到 Postfix 拥有的 data_directory,并记录一条警告。如果您希望继续使用现有数据库文件,请将文件所有权更改为通过 mail_owner 参数指定的账户,并将文件移动到 data_directory,或将其移动到其他 Postfix 拥有的目录中。

注意 2:请勿将此文件放置在可能空间不足的文件系统中。当地址验证表损坏时,系统将无法正常运行,您需要按照下一节的说明手动修复问题。在此期间,您将无法通过 SMTP 接收邮件。

注意 3:verify(8) 守护进程会在不存在数据库时创建新数据库。它会在进入 chroot 监狱前打开或创建该文件。

管理地址验证数据库

verify(8) 手册页描述了控制地址验证结果缓存时长(在需要刷新前)以及结果在过期前可保持"未刷新"状态时长的参数。Postfix 对正结果(地址被接受)和负结果(地址被拒绝,或地址验证因其他原因失败)使用不同的控制机制。

verify(8) 守护进程会定期从地址验证数据库中删除过期条目,并记录保留和删除的条目数量(Postfix 2.7 及更高版本)。当守护进程因"postfix reload"、"postfix stop"或因守护进程在 $max_idle 秒内未收到任何请求而提前终止时,清理操作会被记录为"部分完成"。Postfix 2.6 及更早版本未实现自动地址验证数据库清理。在此情况下,数据库需手动管理,具体如下。

当地址验证数据库文件过大或损坏时,解决方案是手动重命名或删除(注意:不要截断)该文件,然后运行 "postfix reload"。verify(8) 守护进程将创建一个新的数据库文件。

控制地址验证探测的转发路径

默认情况下,Postfix 通过与常规邮件相同的路线发送地址验证探测消息,因为这通常能产生最准确的结果。通过连接到自己的 SMTP 端口来验证本地地址是不可取的,这会触发各种邮件循环警报。对于任何您的机器是最佳 MX 主机的目标,情况也是如此:隐藏域、虚拟域等。

然而,某些站点具有复杂的网络架构,邮件并非直接发送至互联网,而是交由中间中继主机处理。这会导致地址验证问题,因为远程互联网地址仅在 Postfix 能直接访问远程目的地时才能被验证。

为此,Postfix允许你在发送地址验证探测消息时覆盖路由参数。

首先,address_verify_relayhost 参数允许您覆盖 relayhost 设置,而 address_verify_transport_maps 参数允许您覆盖 transport_maps 设置。address_verify_sender_dependent_relayhost_maps 参数对发件人相关的 relayhost 选择具有相同作用。

其次,每个地址类都分配了其对应的地址验证版本的消息传递传输,如下表所示。地址类在 ADDRESS_CLASS_README 文件中定义。

域列表常规传输验证传输
mydestinationlocal_transport地址验证本地传输
虚拟别名域(不适用)(不适用)
虚拟邮箱域虚拟传输address_verify_virtual_transport
relay_domainsrelay_transportaddress_verify_relay_transport
(不适用)default_transportaddress_verify_default_transport

默认情况下,控制地址探测传递的参数与控制正常邮件传递的参数具有相同的值。

强制探测路由示例

在典型场景中,用户会覆盖地址验证探针的 relayhost 设置,并保持其他设置不变:

/etc/postfix/main.cfrelayhost = $mydomain
address_verify_relayhost =
...

位于网络地址转换设备后面的站点可能需要使用一个不同的 SMTP 客户端,以发送正确的主机名信息:

/etc/postfix/main.cfrelayhost = $mydomain
address_verify_relayhost =
address_verify_default_transport = direct_smtp
/etc/postfix/master.cf:
direct_smtp .. .. .. .. .. .. .. .. .. smtp
-o smtp_helo_name=nat.box.tld

强制探测路由的限制

当探测消息未遵循与常规邮件相同的传输路径时,可能会出现不一致的情况。例如,一条消息在遵循常规路径时被接受,而一条完全相同的探测消息在遵循强制路径时被拒绝。相反的情况也可能发生,但可能性较低。