配完 SPF、DKIM、DMARC 之后,邮件终于不进垃圾箱了

前段时间帮朋友的项目排查邮件进垃圾箱的问题,折腾了两个晚上。他 postfix 跑了大半年,往 Gmail 发的邮件十封有八封进垃圾箱,QQ 邮箱好一点但也不稳定。

查下来发现三件套一个都没配。不是他不知道,是觉得"能发出去就行"。这心态其实挺普遍的。

SPF 先说,最容易配也最容易错

SPF 就是一条 DNS TXT 记录,告诉收件方哪些 IP 可以用你的域名发邮件。

他当时的记录是这样的:

v=spf1 include:spf.mail.aliyun.com ~all

但他根本没用阿里邮箱,发信的是 ECS 上的 postfix,走的 ECS 公网 IP。等于 SPF 记录写了个寂寞,对方邮件服务器一查就发现发信 IP 和 SPF 声明对不上。

改成 v=spf1 ip4:他的ECS出口IP ~all,等 DNS 生效之后再发测试邮件,SPF 立刻变成 pass 了。

有几个坑提一下。一个域名只能有一条 SPF 记录,不能加两条 TXT,否则验证直接失败。另外 include 的嵌套总次数不能超过 10 次,DNS 查询太多也会挂。还有 ~all(软拒绝)和 -all(硬拒绝)的区别——前者是标记可疑但不拒收,后者直接拒。建议先用 ~all,等确认没遗漏再改。

DKIM:给邮件加个签名

DKIM 是用私钥给每封邮件签名,收件方拿 DNS 里的公钥验证。能证明两件事:邮件真的从你的域名发出来的,内容没被篡改。

postfix 上用 opendkim 来做:

apt install opendkim opendkim-tools
opendkim-genkey -s mail -d example.com

生成的 mail.txt 内容加到 DNS 里,记录名是 mail._domainkey.example.com。然后配 postfix 连 opendkim 的 socket:

# /etc/postfix/main.cf
milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891

重启两个服务就行。验证方法是发一封邮件看原始邮件头有没有 DKIM-Signature

他一开始配完发现不生效,查了半天发现是 opendkim 的 KeyFile 路径写错了,指向了一个不存在的目录。这种低级错误日志里是有的,但他没看。

DMARC:把前两个串起来

DMARC 建立在 SPF 和 DKIM 之上,干两件事。一是告诉收件方"如果两个都没过你怎么处理",二是给你发验证报告。

配置很简单,一条 DNS TXT 记录:

_dmarc.example.com TXT "v=DMARC1; p=none; rua=mailto:dmarc@example.com"

p=none 表示先不做处理只收集报告。等跑一两周看报告没问题了再改成 quarantine,最终目标是 reject

报告是 XML 格式的,手动看比较费劲。可以用 dmarcian 或者类似的工具可视化。

有一个容易忽略的点:DMARC 要求通过的那个认证(SPF 或 DKIM)必须和 From 头的域名对齐。就是说哪怕 SPF 通过了,但 SPF 检查的域和 From 不是一个域,DMARC 还是不认。

配完之后怎么验证

三个命令:

dig TXT example.com | grep spf
dig TXT mail._domainkey.example.com
dig TXT _dmarc.example.com

然后给 Gmail 发一封测试邮件,看原始邮件头里的 Authentication-Results。理想情况是三个都 pass。

他配完之后发了一轮测试,Gmail 那边从 softfail 变成了全 pass,之前一直进垃圾箱的问题消失了。QQ 邮箱那边也稳定了。

几个容易忘的事

DKIM 的私钥建议定期换。换的时候先加一个新 selector(比如 mail2),DNS 生效之后再停旧的。不要直接删旧的,中间会有一段两个都在用的过渡期。

子域名的 SPF 和 DMARC 不会自动从主域名继承。如果你从 noti.example.com 发邮件,得单独给这个子域名配。

用了第三方发信服务的话(SendGrid、Mailgun 之类),要把它们的 include 加到 SPF 里,DKIM 也需要按它们文档配。否则经过它们发出去的邮件 SPF 和 DKIM 都过不了。


三件套不复杂,半个小时能配完。但很多人要么不知道要配,要么配错了不自知。花点时间检查一遍,比收到用户投诉"没收到邮件"再查要省事得多。