Skip to main content

Appendix F. MTA Relays (MTA 中继)

Appendix F. MTA Relays (MTA 中继)

本附录讨论 MTA 中继场景下的 SPF 实施。

F.1 Relay Types (中继类型)

F.1.1 开放中继(不推荐)

定义: 允许任何人使用的邮件中继

SPF 问题:

- 开放中继是垃圾邮件的主要来源
- 无法验证原始发件人
- SPF 记录无法覆盖所有可能的发件人

建议: 不要运行开放中继

F.1.2 认证中继

定义: 要求 SMTP AUTH 的中继

配置:

# Postfix 配置
smtpd_sasl_auth_enable = yes
smtpd_relay_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_destination

SPF 配置:

company.com IN TXT "v=spf1 mx ip4:203.0.113.50 -all"

其中 203.0.113.50 是认证中继的 IP。

F.1.3 内部中继

场景: 组织内部的邮件路由

[用户] → [内部中继] → [边界 MTA] → [互联网]

SPF 考虑:

- 仅边界 MTA 的 IP 需要在 SPF 中
- 内部中继不需要 SPF 授权(不直接连接外部)

配置示例:

# 仅边界 MTA
company.com IN TXT "v=spf1 ip4:203.0.113.0/28 -all"

# 203.0.113.0/28 是边界 MTA 的 IP 范围

F.2 SPF with Authenticated Relay (SPF 与认证中继)

F.2.1 标准配置

场景: 用户通过认证中继发送邮件

[用户设备] --[SMTP AUTH]--> [中继] --> [接收方]
MAIL FROM: [email protected]
实际 IP: 中继服务器的 IP

SPF 记录:

company.com IN TXT "v=spf1 ip4:RELAY_IP -all"

Postfix 配置(中继服务器):

# main.cf
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes

# 要求认证才能中继
smtpd_relay_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject_unauth_destination

# 添加 Received-SPF 标头
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891

F.2.2 动态 IP 用户

问题: 用户从动态 IP 地址(家庭、咖啡店等)连接

解决方案: 不在 SPF 中列出用户 IP, 仅列出中继 IP

错误配置(不可行):
company.com IN TXT "v=spf1 ip4:ALL_POSSIBLE_USER_IPS -all"

正确配置:
company.com IN TXT "v=spf1 ip4:RELAY_SERVER_IP -all"

用户配置(邮件客户端):

SMTP 服务器: smtp.company.com
端口: 587 (Submission)
安全: STARTTLS
认证: 用户名/密码

F.3 Submission Servers (提交服务器)

F.3.1 端口 587(推荐)

RFC 6409 定义了邮件提交端口 587

特点:

- 专用于邮件提交(非中继)
- 要求认证
- 强制 STARTTLS
- 与端口 25 分离

Postfix 配置:

# master.cf
submission inet n - n - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject

SPF 配置:

# 提交服务器的 IP
company.com IN TXT "v=spf1 ip4:203.0.113.100 -all"

F.3.2 端口 465(SMTPS)

历史: 原 SMTPS 端口, 现已标准化

特点:

- 隐式 TLS(立即启动 TLS)
- 与 STARTTLS 不同
- 某些邮件客户端首选

配置:

# master.cf
smtps inet n - n - - smtpd
-o syslog_name=postfix/smtps
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes

F.4 Multi-Tier Architecture (多层架构)

F.4.1 架构示例

层级 1: 用户设备
↓ (SMTP AUTH)
层级 2: 提交服务器(内部)
↓ (内部转发)
层级 3: 内容过滤/病毒扫描
↓ (内部转发)
层级 4: 边界 MTA(出站)
↓ (互联网)
目标: 接收方

SPF 配置:

# 仅需要授权层级 4 的 IP
company.com IN TXT "v=spf1 ip4:BORDER_MTA_IPS -all"

F.4.2 内部信任

配置示例(Postfix):

层级 2(提交服务器):

# main.cf
mynetworks = 10.0.0.0/8 # 信任内部网络
smtpd_relay_restrictions =
permit_mynetworks,
permit_sasl_authenticated,
reject

层级 3(内容过滤):

# main.cf
mynetworks = 10.0.1.0/24 # 信任提交服务器
content_filter = smtp-amavis:[127.0.0.1]:10024

层级 4(边界 MTA):

# main.cf
mynetworks = 10.0.2.0/24 # 信任内容过滤器
smtpd_relay_restrictions = permit_mynetworks, reject

F.5 Header Rewriting (标头重写)

F.5.1 保留原始信息

问题: 多层中继可能丢失原始发件人信息

解决方案: 添加自定义标头

Postfix 配置:

# header_checks
/^Received:/ PREPEND X-Original-IP: ${client_address}
/^From:/ PREPEND X-Original-From: $1

结果:

X-Original-IP: 192.168.1.100
X-Original-From: [email protected]
Received: from client ([203.0.113.50])

F.5.2 SRS(Sender Rewriting Scheme)

用于: 转发场景

配置 (PostSRSd):

# 安装 postsrsd
apt-get install postsrsd

# Postfix 配置
sender_canonical_maps = tcp:localhost:10001
recipient_canonical_maps = tcp:localhost:10002

重写示例:

F.6 Relay Authentication Methods (中继认证方法)

F.6.1 SMTP AUTH

机制:

  • PLAIN(明文, 需要 TLS)
  • LOGIN(类似 PLAIN)
  • CRAM-MD5(挑战响应)
  • DIGEST-MD5(更安全)

Postfix + Dovecot SASL:

# main.cf
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth

# Dovecot 配置
service auth {
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
}

F.6.2 IP 白名单

场景: 信任特定 IP 范围

# main.cf
mynetworks =
10.0.0.0/8,
203.0.113.0/24

smtpd_relay_restrictions = permit_mynetworks, reject

SPF 配置:

# 包含所有出站 IP
company.com IN TXT "v=spf1 ip4:203.0.113.0/24 -all"

F.6.3 TLS 客户端证书

高安全场景:

# main.cf
smtpd_tls_ask_ccert = yes
smtpd_tls_req_ccert = yes
smtpd_tls_CAfile = /etc/ssl/certs/ca-certificates.crt

# 基于证书允许中继
smtpd_relay_restrictions =
permit_tls_clientcerts,
reject

F.7 Relay Logging and Monitoring (中继日志和监控)

F.7.1 日志记录

启用详细日志(Postfix):

# main.cf
smtpd_tls_loglevel = 1
smtp_tls_loglevel = 1

# 记录 SASL 认证
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname

日志示例:

postfix/submission/smtpd[12345]: connect from client[192.168.1.100]
postfix/submission/smtpd[12345]: SASL LOGIN authentication succeeded: [email protected]
postfix/submission/smtpd[12345]: client=client[192.168.1.100], sasl_method=LOGIN, [email protected]

F.7.2 监控指标

关键指标:

- 认证成功/失败率
- 每用户发送量
- 每 IP 连接数
- 平均队列时间
- 退信率
- 垃圾邮件检测率

监控脚本:

# 统计认证失败
grep "authentication failed" /var/log/mail.log | wc -l

# 统计认证成功
grep "authentication succeeded" /var/log/mail.log | wc -l

# 按用户统计
grep "sasl_username=" /var/log/mail.log | \
cut -d= -f2 | cut -d, -f1 | sort | uniq -c | sort -rn

F.7.3 异常检测

检测暴力破解:

# fail2ban jail.conf
[postfix-sasl]
enabled = true
filter = postfix-sasl
port = smtp,submission,smtps
logpath = /var/log/mail.log
maxretry = 5
findtime = 600
bantime = 3600

检测异常发送:

# 监控脚本
def detect_anomaly(user, send_count):
# 获取用户的历史平均值
avg = get_user_average(user)

# 如果超过平均值 10 倍
if send_count > avg * 10:
alert(f"User {user} sending anomalously: {send_count} vs avg {avg}")

F.8 Best Practices (最佳实践)

F.8.1 中继安全

检查清单:

□ 禁用开放中继
□ 要求 SMTP AUTH
□ 强制使用 TLS
□ 实施速率限制
□ 监控认证失败
□ 定期审查日志
□ 实施 fail2ban 或类似工具
□ 使用强密码策略

F.8.2 SPF 配置

推荐:

1. 仅授权边界 MTA 的 IP
2. 使用 -all(严格模式)
3. 定期审查授权 IP
4. 监控 SPF 检查结果
5. 配合 DKIM 使用
6. 发布 DMARC 策略

F.8.3 故障排除

常见问题:

问题 1: 用户报告无法发送

检查:
- SMTP AUTH 配置正确
- 用户凭据有效
- 端口未被防火墙阻止
- TLS 证书有效

问题 2: 邮件被接收方拒绝

检查:
- 中继 IP 在 SPF 记录中
- DKIM 签名正确
- 反向 DNS 配置
- IP 不在黑名单中

问题 3: 性能问题

检查:
- 队列大小
- 并发连接数
- DNS 解析时间
- 磁盘 I/O

调试命令:

# 测试 SMTP AUTH
swaks --to [email protected] \
--from [email protected] \
--server smtp.company.com:587 \
--tls \
--auth LOGIN \
--auth-user [email protected] \
--auth-password PASSWORD

# 检查队列
mailq
postqueue -p

# 刷新队列
postqueue -f

# 查看实时日志
tail -f /var/log/mail.log