Postfix SPF、DKIM 与 DMARC:自建邮件服务器指南

在 Ubuntu 或 Debian 上为自建 Postfix 配置 OpenDKIM 签名、SPF 与 DMARC,涵盖 milter 接线、PTR 校验、入站验证和 swaks 测试。
4 分钟阅读
更新于 2026-11-04
平台专区
postfixdkimspfdmarc自建

自己运行 Postfix,就没有服务商替你的发件信誉兜底。接收方对每封邮件只做三项判断:连接 IP 是否有权代表你的域名发信(SPF)、邮件是否带有可验证的签名(DKIM)、两项检查失败时该如何处置(DMARC)。三项里缺任何一项,收件方在评分时都会扣分。配置得当,一台普通 VPS 的送达率不输托管邮箱;配置不当,正文还没被读到就已经被拒。

本文以 Ubuntu 24.04 或 Debian 12 为例,用 OpenDKIM 完成发件签名,随后发布 SPF 与 DMARC,最后补上可选的入站验证。示例域名为 yourdomain.com,服务器 IP 为 203.0.113.10。

准备工作

  • Ubuntu 22.04/24.04 或 Debian 12 的 root 或 sudo 权限,Postfix 已安装且能向外投递。
  • 一个静态公网 IPv4 地址,以及该域名 DNS 区域的管理权限。
  • 能够设置 PTR 记录。在 VPS 上这通常要在服务商控制台或提交工单完成,自己的 DNS 区域里改不了。
  • 出站 TCP 25 端口未被封。不少主机商对新账户默认封锁该端口。

第一步:先修好反向解析

在动 DKIM 之前,先让服务器身份前后一致。IP 的 PTR 必须解析到 Postfix 在 HELO 中宣告的主机名,而这个主机名又要能正解回同一个 IP:

203.0.113.10          PTR   mail.yourdomain.com
mail.yourdomain.com   A     203.0.113.10

用 dig -x 203.0.113.10 +short 和 dig mail.yourdomain.com +short 双向确认,然后在 /etc/postfix/main.cf 中设置 myhostname = mail.yourdomain.com。Gmail 与微软都会把 localhost.localdomain 这类通用 HELO 视为垃圾邮件特征,SPF 再干净也没用。

第二步:用 OpenDKIM 为外发邮件签名

安装 milter 并生成 2048 位密钥。不要用 1024 位:它在多数场景仍能通过,但越来越多接收方将其视为弱密钥。

sudo apt install opendkim opendkim-tools
sudo mkdir -p /etc/opendkim/keys/yourdomain.com
sudo opendkim-genkey -b 2048 -d yourdomain.com -s mail \
  -D /etc/opendkim/keys/yourdomain.com
sudo chown -R opendkim:opendkim /etc/opendkim/keys

在 /etc/opendkim.conf 中接线:

Domain           yourdomain.com
KeyTable         /etc/opendkim/KeyTable
SigningTable     refile:/etc/opendkim/SigningTable
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
InternalHosts    refile:/etc/opendkim/TrustedHosts
Socket           inet:8891@localhost
UserID           opendkim

三个表文件决定签哪些邮件、哪些主机可以经过这个 milter:

KeyTable:
mail._domainkey.yourdomain.com  yourdomain.com:mail:/etc/opendkim/keys/yourdomain.com/mail.private

SigningTable:
*@yourdomain.com  mail._domainkey.yourdomain.com

TrustedHosts:
127.0.0.1
::1
localhost

启动服务,并让 Postfix 指向该 socket:

sudo systemctl enable --now opendkim
sudo postconf -e "smtpd_milters = inet:127.0.0.1:8891"
sudo postconf -e "non_smtpd_milters = inet:127.0.0.1:8891"
sudo postconf -e "milter_default_action = accept"
sudo systemctl reload postfix

改完先别急着发测试信。用 systemctl status opendkim 确认服务处于 active (running),再用 journalctl -u opendkim -n 20 看有没有读取密钥失败的报错。私钥文件必须对 opendkim 用户可读,否则 Postfix 会照常收下邮件,签名却被静默跳过——这是"配置看着都对、收件方却说没签名"的最常见原因。

把 mail.txt 里的公钥发布出去。要合并成一条字符串——文件里的括号和换行是 zone 文件语法,不属于记录值:

mail._domainkey.yourdomain.com  TXT  "v=DKIM1; k=rsa; p=MIIBIjANBg..."

第三步:为这台服务器发布 SPF

域名根下一条 TXT 记录即可授权发信 IP。机制名写 ip4,不要写 ipv4:

yourdomain.com  TXT  "v=spf1 ip4:203.0.113.10 a mx ~all"

a 与 mx 用于 IP 变更时仍能覆盖主机名;~all 在验证阶段对其余来源返回软失败。全域名只能有一条 SPF 记录,出现第二条以 v=spf1 开头的 TXT 就会永久报错。用 SPF 检测核对,或用 SPF 生成器拼装。

第四步:发布 DMARC 并读报告

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

从 p=none 起步。两到四周内,接收方会把聚合 XML 报告发到 rua 地址,列出所有以你域名发信的 IP,以及 SPF、DKIM 和对齐是否通过。报告通常每天一封,主题里带域名和接收方名称,附件是 XML 或压缩包。用 DMARC 报告分析器阅读,逐个修好失败的正常来源,再升到 p=quarantine; pct=25,最后才是 p=reject。直接上 reject 是自建者把自己的发票邮件弄丢的经典方式。

第五步:入站验证(可选)

外发认证保护你的信誉,入站检查保护你的用户。SPF 侧安装 postfix-policyd-spf-python,在 /etc/postfix/master.cf 增加服务:

policy-spf  unix  -  n  n  -  -  spawn
  user=nobody argv=/usr/bin/policyd-spf

然后在 smtpd_recipient_restrictions 中、reject_unauth_destination 之后追加 check_policy_service unix:private/policy-spf。入站 DMARC 可安装 opendmarc,socket 设为 inet:127.0.0.1:8893,再追加到 smtpd_milters,两个 milter 按顺序执行。

端到端验证

通过提交端口发一封真实邮件,而不是在本机直接注入:

swaks --to you@gmail.com --from you@yourdomain.com --server mail.yourdomain.com:587 --tls --auth LOGIN --auth-user you@yourdomain.com

在 Gmail 打开显示原始邮件,确认 spf=pass、dkim=pass header.d=yourdomain.com、dmarc=pass。顺手核对签名选择器:这三项应同时带有 header.s=mail,若 s= 与你的 selector 不一致,说明签名的并不是你以为的那把密钥。发布侧用 DKIM 检测(selector 填 mail)、DMARC 查询,以及完整的 邮件健康检查逐项核对。如果邮件直接消失,用 SMTP 测试确认 25 端口可达,再用 黑名单检测排除 IP 被列入。

如果用户从同一台机器上的网页邮箱发信,真正决定认证结果的就是上面这一层 MTA 签名——提交链路的配置见 Roundcube 网页邮件认证,重点是别让信从 localhost 直投导致 SPF 不对齐。

常见错误

  1. Postfix 连不上 milter。因为 milter_default_action = accept,邮件照发但没签名。查 mail.log 里的 connection refused,并看 systemctl status opendkim。
  2. SigningTable 的通配符覆盖不全。*@yourdomain.com 不覆盖子域发件人和部分退信地址,需要为每个发信域显式添加。
  3. DKIM 的 TXT 值带着换行粘贴。DNS 返回被截断的公钥,所有签名都验不过。
  4. SPF 超过 10 次 DNS 查询。整条记录返回 permerror,接收方视同没有 SPF。
  5. PTR 与 HELO 不一致。正向确认的反向解析在内容过滤之前就会被检查。

常见问题

Postfix 已经查 SPF 了,还需要 OpenDMARC 吗?

两者解决的不是同一件事。SPF 检查只告诉你信封发件人是否被授权;OpenDMARC 会执行域名所有者发布的策略,并写入 Authentication-Results 头供垃圾过滤使用。可以省掉,但也就无法遵循其他域名的 reject 策略。

为什么 DKIM 通过了 DMARC 还是失败?

对齐问题。签名必须对信头 From 中的域名有效,而不只是对任意域名有效。如果 SigningTable 用别的 d= 值签名,或者插件在签名后改写了 From,就会出现 DKIM 通过而 DMARC 失败。把邮件头里的 d= 与 From 域名对比即可确认。

可以用 rspamd 替代 OpenDKIM 和 OpenDMARC 吗?

可以。rspamd 在一个进程里完成 DKIM 签名、SPF 与 DMARC 验证,且仍在积极维护;OpenDKIM 2.11 已无上游修复。把 Postfix 指向 11332 端口的 proxy socket 即可。

只用 587 提交,还需要开放 25 端口吗?

直接投递就需要。587 是给你的用户提交邮件用的,服务器仍要通过出站 25 连接对方的 MX 主机。若主机商封锁 25,就改用 smarthost 中继,并把该服务商的 include 域加入 SPF。

这篇指南有帮助吗?