Postfix Access / Relay / Transport 三层策略设计:安全边界、配置原则与性能影响

摘要:Postfix的smptd_access_restrictions(连接层)、relay_domains(中继授权层)和transport_maps(路由层)构成三层独立但关联的策略栈。每一层处理后不同的邮件处理阶段:access在SMTP对话中逐项检查,relay_domains在RCPT TO阶段确定是否中继,transport_map在队列处理阶段决定路由路径。理解各层的时序、安全边界和性能特征,是构建可维护邮件系统的前提。本文提供完整的配置模型和故障排查框架。

1. 三层策略的时序与责任边界

1.1 SMTP会话的检查顺序

Postfix的smtpd(8)进程在完整的SMTP会话中按严格的时序执行检查 [1]:

连接阶段 (smtpd_client_restrictions)
  ↓
EHLO/HELO阶段 (smtpd_helo_restrictions)
  ↓
MAIL FROM阶段 (smtpd_sender_restrictions)
  ↓
RCPT TO阶段 (smtpd_recipient_restrictions) ← 最密集
  ↓  ┌─── relay_domains 在此阶段检查
  ↓  ├─── access 检查 (check_*_access)
  ↓  └─── mydestination 接收判断
  ↓
DATA阶段 (smtpd_data_restrictions)
  ↓
END-OF-MESSAGE (smtpd_end_of_data_restrictions)
  ↓
队列 → clean → pickup → 入站处理完成
  ↓
出站: qmgr → transport (transport_maps决定) → smtp (连接目标MTA)
          ↓
     fallback_relay / relay_transport (无匹配时的回退)

1.2 三层职责总结

策略层配置文件处理阶段主要职责
Accesssmtpd_*_restrictionsSMTP会话(RCPT TO前)连接控制、黑白名单、SPF/DNSBL策略检查
Relayrelay_domainsSMTP会话(RCPT TO时)中继授权边界——确定接收还是中继
Transporttransport_maps队列处理(qmgr调度)路由规则——指定出站邮件的MTA/端口/传输方式

2. Access 层:smtpd_*_restrictions 详解

2.1 UCE检查清单

Postfix的UCE(Unsolicited Commercial Email)检查清单是邮件安全的第一道防线 [1]。推荐的完整配置:

# /etc/postfix/main.cf

# ==== 连接层 ====
smtpd_client_restrictions =
    permit_mynetworks           # 内网IP直接放行
    permit_sasl_authenticated   # SASL认证用户放行
    reject_rbl_client zen.spamhaus.org    # DNSBL实时黑名单
    reject_rbl_client bl.spamcop.net
    reject_rhsbl_client dbl.spamhaus.org
    permit

# ==== EHLO/HELO层 ====
smtpd_helo_required = yes
smtpd_helo_restrictions =
    permit_mynetworks
    reject_invalid_helo_hostname
    reject_non_fqdn_helo_hostname      # HELO必须是完整域名
    check_helo_access hash:/etc/postfix/helo_access
    permit

# ==== MAIL FROM层 ====
smtpd_sender_restrictions =
    permit_mynetworks
    permit_sasl_authenticated
    reject_non_fqdn_sender              # 发件地址必须是FQDN
    reject_unknown_sender_domain        # 域名必须可解析
    check_sender_access hash:/etc/postfix/sender_access
    permit

# ==== RCPT TO层(最密集) ====
smtpd_recipient_restrictions =
    permit_mynetworks
    permit_sasl_authenticated
    reject_unauth_destination           # 核心:防止开放中继
    reject_non_fqdn_recipient
    reject_unknown_recipient_domain
    check_recipient_access hash:/etc/postfix/recipient_access
    reject_unverified_bounce            # BATV退信验证(如启用)
    reject_unauth_pipelining
    permit

2.2 check_*_access 表设计原则

access表(hash格式)支持域名级、邮箱级的多粒度匹配 [2]:

# /etc/postfix/recipient_access
example.com               OK            # 无条件接收
spamdomain.com            REJECT        # 整域拒绝
user@malicious.com        550 5.1.1 Blocked by policy
@badhosting.net           REJECT        # 子域通配

# /etc/postfix/sender_access
goodpartner.com           OK
bulkmailer.example.com    REJECT        # 仅拒绝该发件人

# 注意:access表是正则非贪婪匹配——@badhosting.net
# 匹配所有以 badhosting.net 结尾的地址

2.3 Access层性能特征

3. Relay层:relay_domains 安全边界

3.1 收件与中继的本质区别

Postfix根据RCPT TO地址决定是本地接收(投递到mydestination指定的域)还是中继(转发出站)。relay_domains定义了哪些域是"授权代收"的——通常是托管域的MX目标。安全模型 [1]:

3.2 relay_domains 配置示例

# /etc/postfix/main.cf
myhostname = mail.example.com
mydomain = example.com
myorigin = $mydomain

# 本地接收域
mydestination = $myhostname, localhost.$mydomain, localhost, $mydomain

# 授权中继域(邮件托管服务)
relay_domains = $mydomain
relay_domains = hash:/etc/postfix/relay_domains   # 或使用数据库

# 中继域查找
relay_recipient_maps = hash:/etc/postfix/relay_recipients

# relay_domains 数据库文件
# /etc/postfix/relay_domains:
# example.net           OK
# partner-company.com   OK

3.3 中继授权的安全陷阱

4. Transport层:transport_maps 出站路由

4.1 路由规则语法

transport_maps在队列处理(qmgr)阶段查询,决定出站邮件的传输通道 [1]:

# /etc/postfix/transport 格式:
# domain transport:nexthop
domain                  transport:nexthop

# 示例
example.com             smtp:mail.example.com       # 通过指定MX中继
[198.51.100.10]         smtp:[10.0.0.1]:2525        # IP+端口覆盖
.example.net            relay:                      # 子域通配,使用relay传输
*                       smtp:fallback.example.com   # 默认回退

4.2 生产级transport配置

# /etc/postfix/main.cf
transport_maps = hash:/etc/postfix/transport

# 默认传输(transport_maps未匹配时)
default_transport = smtp

# relay传输(当relay_domains匹配且无transport_maps时)
relay_transport = relay

# 当所有出站尝试失败时(延迟而非退回)
fallback_transport_maps = hash:/etc/postfix/fallback_transport
# fallback_relay通常用于智能主机场景

# /etc/postfix/transport:
# 将发往 partner.com 的邮件通过专用中继发送
partner.com             smtp:partner-relay.example.com:587

# 发往特定域的邮件限制并发数
# 通过master.cf中定义多个smtp实例,每个配置不同的process_limit
highvolume.example.com  smtp-fast:

# 将某个域的全部邮件转发到另一个MTA(内部MTLS流量)
internal-corp.com       smtp:[10.0.10.100]:2525

# 更新transport映射
postmap /etc/postfix/transport

4.3 关于[braket](字面量)的行为

transport表中的[braket]语法(如smtp:[10.0.0.1])指示Postfix不进行MX查询,直接将邮件发送至指定的IP/主机名。未使用[]时,Postfix会对此主机进行MX解析(可能覆盖意图)。此机制在RFC 5321 §5.1(地址解析顺序)中有详细描述 [4]。区别:

# 无[] — 进行MX/A/AAAA查询
smtp:mail.example.com
# → Postfix 查询 mail.example.com 的 MX
#   → 如果存在MX则使用MX;否则使用A/AAAA记录

# 有[] — 跳过MX查询,直接连接
smtp:[mail.example.com]
# → 跳过MX查询,直接A/AAAA解析 mail.example.com

smtp:[198.51.100.10]
# → 字面量IP,直接连接

5. 三层策略的交互与故障排查

5.1 决策树

SMTP RCPT TO 到达
  ↓
是否在 mydestination 中?
  ├── 是 → 本地投递(不检查relay_domains)
  └── 否 → 继续
      ↓
reject_unauth_destination 是否触发?
  ├── 是 → 550拒绝(除非在relay_domains中)
  └── 否 → 继续
      ↓
是否在 relay_domains 中?
  ├── 是 → 授权中继
  │   ↓
  │  transport_maps有匹配?
  │   ├── 是 → 使用指定传输通道
  │   └── 否 → relay_transport(默认)
  │       ↓
  │  fallback_relay → MX解析 → 出站连接
  └── 否 → 550拒绝(非授权中继)

5.2 故障排查命令

# 模拟邮件检查(不发送)
postmap -q "user@example.com" hash:/etc/postfix/recipient_access
postmap -q "example.com" hash:/etc/postfix/transport
postmap -q "example.com" hash:/etc/postfix/relay_domains

# 验证中继授权
postconf -n | grep -E 'relay_domain|mydestination|reject_unauth'

# 检查transport映射是否加载
postconf -n | grep transport_maps
postmap -s hash:/etc/postfix/transport

# 日志分析
grep "relay=" /var/log/mail.log | tail -20
grep "transport=" /var/log/mail.log | tail -20

# 检查邮件是否因relay拒绝
grep "relay access denied" /var/log/mail.log | tail -10

6. 性能影响分析

6.1 各层开销对比

操作平均延迟缓存生效并发影响
hash表查询(mmap)~10μs隐式(mmap页面缓存)
regexp表匹配(10条)~50μs不适用
DNSBL查询(无缓存)50-500msncache/scache显著
relay_domains数据库查询~100μs-1msproxymap共享
transport_maps查询~10μs-1msqmgr内部缓存低(队列处理阶段)

6.2 Access层过载保护

smtpd_client_restrictions在其第一阶段(DNSBL查询)可能造成过载。如果DNSBL服务不可用,Postfix会阻塞连接建立。配置了多个DNSBL的服务器的典型问题是:一个慢速的DNSBL延迟了整个入站邮件接收。设置smtpd_client_restrictions的超时和跳过的机制 [5]:

# 设置DNSBL查询超时(避免依赖外部服务导致阻塞)
smtpd_client_event_limit_exceptions = $mynetworks
smtpd_client_connection_count_limit = 10
smtpd_client_connection_rate_limit = 30

# 使用postscreen前检查(Postfix 2.8+)减轻smtpd压力
# 将部分检查前置到postscreen
postscreen_dnsbl_threshold = 3
postscreen_dnsbl_sites = zen.spamhaus.org*3
    bl.spamcop.org*2
    dbl.spamhaus.org*1

7. 多层协同设计实例

7.1 典型多租户邮件中继设计

# 场景:邮件服务平台,为500个客户域提供邮件中继和托管

# Access层 — 客户专属连接策略
smtpd_client_restrictions =
    (hash) /etc/postfix/customer_whitelist  # 客户MTA IP白名单
    permit_mynetworks
    reject_rbl_client zen.spamhaus.org
    permit

# Relay层 — 客户域映射
relay_domains = hash:/etc/postfix/relay_domains
relay_recipient_maps = hash:/etc/postfix/relay_recipients

# Transport层 — 按客户路由到专用出站IP池
transport_maps = hash:/etc/postfix/transport
# /etc/postfix/transport:
# customer1.com  smtp:outbound-1.example.com:25
# customer2.com  smtp:outbound-2.example.com:25

参考文献

  1. Postfix Architecture Documentation (Postfix 3.9). W. Venema. . §SMTPD ACCESS POLICY (the UCE checklist and restriction evaluation order), §RELAY AND MAIL DELIVERY (relay_domains, transport_maps design).
  2. Postfix access(5) Manual Page. §Restriction classes and table-driven access control mechanisms.
  3. RFC 2505 — Anti-Spam Recommendations for SMTP MTAs. G. Lindberg. February 1999. §2.3 (Open Relay Prevention — the highest priority anti-spam measure). BCP 30.
  4. RFC 5321 — Simple Mail Transfer Protocol. J. Klensin. October 2008. §5.1 (Mail Delivery Path — Address resolution order: MX follows MX preference, CNAME prohibition, literal IP bypass).
  5. Postfix postscreen(8) Manual Page. W. Venema. §DNSBL-based connection pre-filtration architecture and DNSBL timeout behavior.
  6. RFC 7505 — A "Null MX" No Service Resource Record for Domains that Accept No Mail. J. Levine. June 2015. §2 (relay control implications for domains that do not wish to receive mail).