强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

SMTP 服务器搭建完全指南 / 第 7 章:DKIM 邮件签名

第 7 章:DKIM 邮件签名

DKIM 是邮件世界的数字签名——证明邮件确实来自声称的域名,且未被篡改。


7.1 DKIM 概述

7.1.1 什么是 DKIM

DKIM(DomainKeys Identified Mail)是一种邮件认证技术,通过公钥密码学对邮件进行数字签名。接收方可以通过 DNS 查询发件域名的公钥来验证签名。

核心原理

发送方                              接收方
    │                                   │
    │  1. 生成邮件                       │
    │  2. 用私钥对邮件头和正文签名        │
    │  3. 将签名添加到邮件头 (DKIM-Signature)  │
    │──── 发送邮件 ────────────────────►│
    │                                   │  4. 从 DKIM-Signature 中提取域名和选择器
    │                                   │  5. 查询 DNS TXT: selector._domainkey.domain
    │                                   │  6. 获取公钥
    │                                   │  7. 用公钥验证签名
    │                                   │  8. 验证通过 → 邮件可信

7.1.2 DKIM 的作用

作用说明
身份验证证明邮件来自域名持有者授权的服务器
完整性保护确保邮件在传输过程中未被篡改
品牌保护防止冒用域名发送钓鱼邮件
送达率提升通过验证的邮件更少被标记为垃圾

7.1.3 DKIM 与其他认证技术的关系

技术验证内容与 DKIM 的关系
SPF发件 IP 是否被授权互补,验证不同维度
DKIM邮件签名是否有效核心认证机制
DMARCSPF/DKIM 与域名对齐策略框架,依赖 SPF 和 DKIM

7.2 安装 OpenDKIM

7.2.1 安装软件包

# Ubuntu / Debian
sudo apt install -y opendkim opendkim-tools

# RHEL / Rocky Linux
sudo dnf install -y opendkim opendkim-tools

# 检查安装
opendkim -V

7.2.2 创建目录结构

# 创建密钥目录
sudo mkdir -p /etc/opendkim/keys/example.com

# 设置权限
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod -R 750 /etc/opendkim/keys

7.3 生成 DKIM 密钥

7.3.1 生成密钥对

# 生成 DKIM 密钥对
# -s: 选择器名称
# -d: 域名
# -b: 密钥长度(2048 位推荐)
sudo opendkim-genkey -s mail -d example.com -b 2048

# 移动密钥文件
sudo mv mail.private /etc/opendkim/keys/example.com/
sudo mv mail.txt /etc/opendkim/keys/example.com/

# 设置密钥权限
sudo chown opendkim:opendkim /etc/opendkim/keys/example.com/mail.private
sudo chmod 600 /etc/opendkim/keys/example.com/mail.private

7.3.2 密钥生成参数说明

参数说明推荐值
-s选择器名称mail, default, selector1
-d域名你的邮件域名
-b密钥位数2048(最低 1024)
-D输出目录/etc/opendkim/keys/

7.3.3 查看生成的 DNS 记录

# 查看需要添加到 DNS 的 TXT 记录
sudo cat /etc/opendkim/keys/example.com/mail.txt

# 输出示例:
# mail._domainkey IN TXT (
#   "v=DKIM1; h=sha256; k=rsa; "
#   "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
#   "..." )

7.4 配置 OpenDKIM

7.4.1 主配置文件

# /etc/opendkim.conf — OpenDKIM 主配置

# 基础设置
AutoRestart            Yes
AutoRestartRate        10/1h
Syslog                 yes
SyslogSuccess          Yes
LogWhy                 Yes

# 监听地址
Socket                 inet:8891@localhost
# 或使用 Unix socket:
# Socket                 local:/var/spool/postfix/opendkim/opendkim.sock

# 用户和组
UserID                 opendkim:opendkim

# 签名表
SigningTable            refile:/etc/opendkim/SigningTable
KeyTable                refile:/etc/opendkim/KeyTable
InternalHosts           /etc/opendkim/TrustedHosts

# 签名算法
Canonicalization        relaxed/simple
Mode                    sv
SubDomains              No
AutoRestart             Yes
Background              Yes
DNSTimeout              5
SignatureAlgorithm      rsa-sha256

# 头部签名
OversignHeaders         From

# 忽略的头部
IgnoreMalformedMail     Yes

7.4.2 签名表配置

# /etc/opendkim/SigningTable
# 签名表:发件域名 → 密钥选择器

# 单域名
*@example.com    mail._domainkey.example.com

# 多域名
*@example.com    mail._domainkey.example.com
*@example.org    mail._domainkey.example.org

# 使用正则表达式
*@*              mail._domainkey.$2

7.4.3 密钥表配置

# /etc/opendkim/KeyTable
# 密钥表:选择器域名 → 密钥文件路径

# 格式:选择器._domainkey.域名    域名:选择器:密钥路径
mail._domainkey.example.com    example.com:mail:/etc/opendkim/keys/example.com/mail.private

# 多域名
mail._domainkey.example.com    example.com:mail:/etc/opendkim/keys/example.com/mail.private
mail._domainkey.example.org    example.org:mail:/etc/opendkim/keys/example.org/mail.private

7.4.4 可信主机配置

# /etc/opendkim/TrustedHosts
# 这些来源的邮件需要签名

127.0.0.1
localhost
10.0.0.0/8
172.16.0.0/12
192.168.0.0/16
203.0.113.10/32

# 域名形式
.example.com

7.4.5 设置目录权限

# 确保 OpenDKIM 可以访问密钥和配置
sudo chown -R opendkim:opendkim /etc/opendkim
sudo chmod 640 /etc/opendkim/keys/example.com/mail.private

# 创建 Postfix 可访问的 socket 目录
sudo mkdir -p /var/spool/postfix/opendkim
sudo chown opendkim:postfix /var/spool/postfix/opendkim

7.5 集成 Postfix

7.5.1 配置 Postfix 使用 OpenDKIM

# /etc/postfix/main.cf — DKIM 配置

# 使用 inet socket
milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891

# 或使用 Unix socket
# smtpd_milters = local:/var/spool/postfix/opendkim/opendkim.sock
# non_smtpd_milters = local:/var/spool/postfix/opendkim/opendkim.sock

7.5.2 启动服务

# 启动 OpenDKIM
sudo systemctl enable --now opendkim

# 重新加载 Postfix
sudo systemctl reload postfix

# 检查 OpenDKIM 状态
sudo systemctl status opendkim

# 查看日志
sudo tail -f /var/log/mail.log | grep -i dkim

7.6 配置 DNS 记录

7.6.1 添加 DKIM TXT 记录

从生成的 mail.txt 文件中提取 DNS 记录内容,添加到 DNS 管理面板:

; DKIM TXT 记录
; 主机记录: mail._domainkey
; 类型: TXT
; 值: 从 mail.txt 文件中提取

mail._domainkey.example.com. IN TXT "v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...(完整公钥)..."

7.6.2 DNS 记录格式说明

字段说明示例
vDKIM 版本DKIM1
h哈希算法sha256
k密钥类型rsa
s服务类型(可选)email
t标记(可选)y(测试模式), s(严格子域名)
p公钥(Base64)MIIBIjAN...

7.6.3 验证 DNS 记录

# 查询 DKIM DNS 记录
dig TXT mail._domainkey.example.com +short

# 使用 opendkim-testkey 验证
sudo opendkim-testkey -d example.com -s mail -vvv
# 期望输出: key OK

# 使用在线工具
# https://mxtoolbox.com/dkim.aspx

7.7 测试 DKIM 签名

7.7.1 发送测试邮件

# 发送测试邮件
echo "DKIM 测试邮件" | mail -s "DKIM Test" user@example.com

# 使用 swaks 测试
swaks --to test@check-auth@verifier.port25.com \
      --from admin@example.com \
      --server mail.example.com \
      --port 587 \
      --auth-user admin@example.com \
      --auth-password "password" \
      -tls

# 检查返回的验证报告

7.7.2 验证 DKIM 签名

# 检查邮件头中的 DKIM-Signature
# 查看收到的邮件原始内容
cat /var/mail/vhosts/example.com/admin/new/* | grep -A20 "DKIM-Signature"

# 使用 opendkim-testmsg
cat test-email.eml | opendkim-testmsg

7.7.3 DKIM 签名字段解读

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple;
    d=example.com; s=mail;
    h=from:to:subject:date:message-id;
    bh=abc123...=;  ← 正文哈希
    b=xyz789...=    ← 签名值
字段说明
v签名版本
a签名算法
c规范化方法
d签名域名
s选择器
h签名的头部列表
bh正文哈希值
b签名值

7.8 多域名 DKIM 配置

7.8.1 为多个域名生成密钥

#!/bin/bash
# gen-dkim-keys.sh — 批量生成 DKIM 密钥

SELECTOR="mail"
KEY_LENGTH=2048
DOMAINS="example.com example.org example.net"

for DOMAIN in $DOMAINS; do
    echo "=== 生成 $DOMAIN 的 DKIM 密钥 ==="
    
    # 创建目录
    mkdir -p /etc/opendkim/keys/$DOMAIN
    
    # 生成密钥
    opendkim-genkey -s $SELECTOR -d $DOMAIN -b $KEY_LENGTH
    
    # 移动文件
    mv ${SELECTOR}.private /etc/opendkim/keys/$DOMAIN/
    mv ${SELECTOR}.txt /etc/opendkim/keys/$DOMAIN/
    
    # 设置权限
    chown -R opendkim:opendkim /etc/opendkim/keys/$DOMAIN
    chmod 600 /etc/opendkim/keys/$DOMAIN/${SELECTOR}.private
    
    echo "✅ $DOMAIN 密钥已生成"
    echo "DNS 记录:"
    cat /etc/opendkim/keys/$DOMAIN/${SELECTOR}.txt
    echo ""
done

7.8.2 更新签名表和密钥表

# /etc/opendkim/SigningTable
*@example.com    mail._domainkey.example.com
*@example.org    mail._domainkey.example.org
*@example.net    mail._domainkey.example.net

# /etc/opendkim/KeyTable
mail._domainkey.example.com    example.com:mail:/etc/opendkim/keys/example.com/mail.private
mail._domainkey.example.org    example.org:mail:/etc/opendkim/keys/example.org/mail.private
mail._domainkey.example.net    example.net:mail:/etc/opendkim/keys/example.net/mail.private

# /etc/opendkim/TrustedHosts
127.0.0.1
localhost
.example.com
.example.org
.example.net

7.9 密钥轮换

7.9.1 为什么需要密钥轮换

原因说明
安全最佳实践定期更换密钥降低泄露风险
合规要求某些安全标准要求定期轮换
密钥泄露怀疑密钥泄露时需要立即更换

7.9.2 密钥轮换步骤

#!/bin/bash
# rotate-dkim-key.sh — DKIM 密钥轮换脚本

DOMAIN="example.com"
OLD_SELECTOR="mail"
NEW_SELECTOR="mail2"
KEY_LENGTH=2048

echo "=== DKIM 密钥轮换 ==="
echo "域名: $DOMAIN"
echo "旧选择器: $OLD_SELECTOR"
echo "新选择器: $NEW_SELECTOR"

# 1. 生成新密钥
echo "[1/4] 生成新密钥..."
mkdir -p /etc/opendkim/keys/$DOMAIN
opendkim-genkey -s $NEW_SELECTOR -d $DOMAIN -b $KEY_LENGTH
mv ${NEW_SELECTOR}.private /etc/opendkim/keys/$DOMAIN/
mv ${NEW_SELECTOR}.txt /etc/opendkim/keys/$DOMAIN/
chown opendkim:opendkim /etc/opendkim/keys/$DOMAIN/${NEW_SELECTOR}.private
chmod 600 /etc/opendkim/keys/$DOMAIN/${NEW_SELECTOR}.private

# 2. 更新配置
echo "[2/4] 更新配置..."
sed -i "s|${OLD_SELECTOR}\._domainkey.${DOMAIN}|${NEW_SELECTOR}._domainkey.${DOMAIN}|g" /etc/opendkim/SigningTable
sed -i "s|${OLD_SELECTOR}\._domainkey.${DOMAIN}|${NEW_SELECTOR}._domainkey.${DOMAIN}|g" /etc/opendkim/KeyTable

# 3. 重载 OpenDKIM
echo "[3/4] 重载服务..."
systemctl reload opendkim

# 4. 提示添加新 DNS 记录
echo "[4/4] 请添加以下 DNS 记录:"
cat /etc/opendkim/keys/$DOMAIN/${NEW_SELECTOR}.txt
echo ""
echo "⚠️ 注意:请先添加新 DNS 记录,等待 DNS 传播完成后再删除旧记录"

echo ""
echo "=== 轮换完成 ==="

7.10 业务场景:SaaS 平台 DKIM 配置

场景描述

一个 SaaS 平台需要为每个租户配置独立的 DKIM 签名。

自动化方案

#!/bin/bash
# saas-dkim-setup.sh — SaaS 平台 DKIM 自动配置

TENANT_DOMAIN="$1"
SELECTOR="app"

if [ -z "$TENANT_DOMAIN" ]; then
    echo "用法: $0 <tenant-domain>"
    exit 1
fi

# 生成密钥
opendkim-genkey -s $SELECTOR -d $TENANT_DOMAIN -b 2048 -D /etc/opendkim/keys/$TENANT_DOMAIN/

# 更新签名表
echo "*@${TENANT_DOMAIN}    ${SELECTOR}._domainkey.${TENANT_DOMAIN}" >> /etc/opendkim/SigningTable

# 更新密钥表
echo "${SELECTOR}._domainkey.${TENANT_DOMAIN}    ${TENANT_DOMAIN}:${SELECTOR}:/etc/opendkim/keys/${TENANT_DOMAIN}/${SELECTOR}.private" >> /etc/opendkim/KeyTable

# 更新可信主机
echo ".${TENANT_DOMAIN}" >> /etc/opendkim/TrustedHosts

# 设置权限
chown -R opendkim:opendkim /etc/opendkim/keys/$TENANT_DOMAIN
chmod 600 /etc/opendkim/keys/$TENANT_DOMAIN/${SELECTOR}.private

# 重载服务
systemctl reload opendkim

# 输出 DNS 记录
echo "=== 请为 $TENANT_DOMAIN 添加以下 DNS 记录 ==="
cat /etc/opendkim/keys/$TENANT_DOMAIN/${SELECTOR}.txt

7.11 注意事项

⚠️ 密钥安全

  • 私钥文件权限必须是 600,仅 OpenDKIM 用户可读
  • 不要将私钥提交到版本控制系统
  • 密钥泄露时立即轮换

⚠️ DNS 记录长度

  • 2048 位 RSA 密钥的 DNS TXT 记录较长
  • 某些 DNS 提供商对 TXT 记录长度有限制(通常 255 字符)
  • 超长记录需要分段:
mail._domainkey.example.com. IN TXT (
    "v=DKIM1; h=sha256; k=rsa; p=MIIBIjANBgkq..."
    "...HkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
)

💡 DKIM 测试模式

  • 在 DNS TXT 记录中添加 t=y 表示测试模式
  • 测试模式下,验证失败不会影响邮件投递
  • 配置完成后移除 t=y

7.12 扩展阅读


上一章← 第 6 章:反垃圾邮件策略 下一章第 8 章:SPF 发件人策略框架 →