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).

引用本文

ztpop.net 知识库编辑. "Postfix Access / Relay / Transport 三层策略设计:安全边界、配置原则与性能影响" ztpop.net 知识库.

本站技术文章采用 CC-BY 4.0 许可,可自由引用,仅需标注来源 ztpop.net。