跳转至内容

OpenDKIM

来自 ArchWiki

OpenDKIMDomainKeys Identified Mail (DKIM) 发件人身份验证系统的开源实现。

DKIM 得到了大多数常用邮件提供商的支持,包括 Yahoo、Google 和 Outlook.com。

原理

基本上,DKIM 对来自服务器的所有邮件进行数字签名,以验证邮件确实是从指定域发送的,并且没有被伪造或修改。

  • 发件人的邮件服务器使用私钥对传出的电子邮件进行签名。
  • 当邮件到达时,接收者(或其服务器)会从域的 TXT 记录中读取公钥并验证签名。

这确保了邮件是从其私钥与域的公钥匹配的服务器发送的。

有关更多信息,请参阅 RFC 6376

安装

安装 opendkim 包。

配置

签名服务的主要配置文件是 /etc/opendkim/opendkim.conf

  • 将示例配置文件 /usr/share/doc/opendkim/opendkim.conf.sample 复制/移动到 /etc/opendkim/opendkim.conf,然后更改以下选项
/etc/opendkim/opendkim.conf
Domain                  example.com
KeyFile                 /path/to/keys/server1.private
Selector                myselector
Socket                  inet:8891@localhost
UserID                  opendkim
  • Socket 地址与 /etc/postfix/main.cf 中指定的地址相同。/etc/postfix/main.cf 应包含以下内容
/etc/postfix/main.cf
# For use by dkim milter
smtpd_milters = inet:localhost:8891
non_smtpd_milters = $smtpd_milters
milter_default_action = accept
  • 要生成私有签名密钥,请指定用于发送邮件的域和一个用于引用密钥的选择器。选择器可以是任何值。有关详细信息,请参阅 RFC,但字母数字字符串应该是可以的
$ opendkim-genkey -r -s myselector -d example.com
  • 有时邮件在传输过程中会被重新格式化(例如,制表符被替换为空格),导致 DKIM 签名无效。为防止琐碎的重新格式化破坏标题和正文的信任,存在 *规范化*,它是一种策略,说明格式应如何严格保持。可用设置包括 *simple*(不允许重新格式化)和 *relaxed*(允许一些重新格式化)。有关详细信息,请参阅 RFC 4871 3.4。这些可以单独为标题和正文设置
/etc/opendkim/opendkim.conf
...
Canonicalization        relaxed/simple
...

此示例允许对标题进行一些重新格式化,但不对正文进行。opendkim 的默认设置为 *simple/simple*。

  • 还有其他配置选项可用。请务必阅读文档。
  • 启用/启动 opendkim.service

DNS 记录

添加一个带有选择器和公钥的 **DNS TXT** 记录。正确的记录是用私钥生成的,可以在私钥相同的目录中的 myselector.txt 中找到。

示例

myselector._domainkey   IN	 TXT	"v=DKIM1; p=...................."

记录还有其他几个可用开关(请参阅 RFC 4871 3.6.1),最有趣的是 t=y,它启用了测试模式,指示检查接收者,邮件不得与未签名邮件有任何不同,无论签名状态如何。

检查 DNS 记录是否已正确更新

$ host -t TXT myselector._domainkey.example.com

您还可以使用网上提供的 DKIM 密钥检查器之一来检查 DKIM DNS 记录是否格式正确。

Postfix 集成

将以下行添加到 main.cf

/etc/postfix/main.cf
non_smtpd_milters=inet:127.0.0.1:8891
smtpd_milters=inet:127.0.0.1:8891

要集成 DKIM 和 DMARC,请改用 Unix 套接字,设置 UMask 以给予组对套接字的写入权限,并将 postfix 用户添加到 opendkim 组

/etc/opendkim/opendkim.conf
Socket                  local:/run/opendkim/opendkim.sock
UMask                   002
/etc/postfix/main.cf
non_smtpd_milters = unix:/run/opendkim/opendkim.sock,
                    unix:/run/opendmarc/opendmarc.sock
smtpd_milters = unix:/run/opendkim/opendkim.sock,
                unix:/run/opendmarc/opendmarc.sock

或更改 master.cf 中的 smtpd 选项

/etc/postfix/master.cf
smtp      inet  n       -       n       -       -       smtpd
  -o smtpd_client_connection_count_limit=10
  -o smtpd_milters=inet:127.0.0.1:8891

submission inet n       -       n       -       -       smtpd
  -o smtpd_enforce_tls=no
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o smtpd_sasl_path=smtpd
  -o cyrus_sasl_config_path=/etc/sasl2
  -o smtpd_milters=inet:127.0.0.1:8891

Sendmail 集成

编辑 sendmail.mc 文件,并在 **最后一行** 以 FEATURE 开头 **之后** 添加以下行

/etc/mail/sendmail.mc
INPUT_MAIL_FILTER(`opendkim', `S=inet:8891@localhost')

使用以下命令重新构建 sendmail.cf 文件

# m4 /etc/mail/sendmail.mc > /etc/mail/sendmail.cf

然后 重启 sendmail.service

多个域名

要在同一服务器上为多个虚拟域提供邮件服务器服务,请按如下方式修改基本配置

提供以下指令

/etc/opendkim/opendkim.conf
KeyTable                refile:/etc/opendkim/KeyTable
SigningTable            refile:/etc/opendkim/SigningTable
ExternalIgnoreList      refile:/etc/opendkim/TrustedHosts
InternalHosts           refile:/etc/opendkim/TrustedHosts
注意 多个域可以共享一个 DKIM 密钥。

创建以下两个文件,以告知 opendkim 在何处查找正确的密钥。您可以为所有域使用相同的密钥,也可以为每个域生成一个密钥。请根据您的设置进行更改。根据需要添加更多行。在以下示例中,example1.comexample3.com 共享同一个密钥。

/etc/opendkim/KeyTable
 
myselector._domainkey.example1.com example1.com:myselector:/etc/opendkim/keys/domain1.com/myselector.private
myselector._domainkey.example2.com example2.com:myselector:/etc/opendkim/keys/domain2.com/myselector.private
myselector._domainkey.example3.com example3.com:myselector:/etc/opendkim/keys/domain1.com/myselector.private
...
/etc/opendkim/SigningTable
*@example1.com myselector._domainkey.example1.com
*@example2.com myselector._domainkey.example2.com
*@example3.com myselector._domainkey.example3.com
...

现有的 /etc/opendkim/TrustedHosts 文件会告知 opendkim 谁可以使用密钥。这由配置文件中的 ExternalIgnoreList 指令引用。Opendkim 在验证传入邮件时会忽略此主机列表。

由于此列表也被 InternalHosts 指令引用,因此同一列表中的主机将被视为“内部”,opendkim 会对其传出的邮件进行签名。请记住将 <server_ip> 更改为正确的服务器 IP 地址

/etc/opendkim/TrustedHosts
127.0.0.1
::1
localhost
<server_ip>
hostname.example1.com
example1.com
hostname.example2.com
example2.com
...

将所有文件的所有权更改为 opendkim

# chown -R opendkim:mail /etc/opendkim

为每个域添加一个带有选择器和公钥的 DNS TXT 记录。

您现在可以 重启 opendkim。

安全

OpenDKIM 守护进程的默认配置从安全角度来看并不理想(所有这些都是小安全问题)

  • OpenDKIM 守护进程根本不需要以 root 身份运行(前面建议的配置将使 OpenDKIM 自身放弃 root 权限,但 systemd 也可以做到这一点,而且更早)。
  • 如果您的邮件守护进程与 OpenDKIM 守护进程位于同一主机上,则不需要 localhost TCP 套接字,而是可以使用 Unix 套接字,从而允许经典的用户名/组访问控制。
  • OpenDKIM 默认使用 /tmp 文件夹,而它可以改用具有额外访问限制的自己的文件夹。
注意 此示例适用于单个域设置。

以下配置文件将解决其中大多数问题(假设您使用的是 Postfix),并在 systemd 服务单元中删除一些不必要的选项

/etc/opendkim/opendkim.conf
BaseDirectory           /var/lib/opendkim
Domain                  example.com
KeyFile                 /etc/opendkim/myselector.private
Selector                myselector
Socket                  local:/run/opendkim/opendkim.sock
Syslog                  Yes
TemporaryDirectory      /run/opendkim
UMask                   002
/etc/systemd/system/opendkim.service
[Unit]
Description=OpenDKIM daemon
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
User=opendkim
Group=postfix
ExecStart=/usr/bin/opendkim -x /etc/opendkim/opendkim.conf
RuntimeDirectory=opendkim
RuntimeDirectoryMode=0750

[Install]
WantedBy=multi-user.target

相应地编辑 /etc/postfix/main.cf,使 Postfix 监听此 Unix 套接字

/etc/postfix/main.cf
smtpd_milters = unix:/run/opendkim/opendkim.sock
non_smtpd_milters = unix:/run/opendkim/opendkim.sock

故障排除

错误: "milter-reject: END-OF-MESSAGE from localhost"

很可能是 Postfix milter 协议在 /etc/postfix/main.cf 中设置错误

# Postfix ≥ 2.6
milter_protocol = 6
# 2.3 ≤ Postfix ≤ 2.5
milter_protocol = 2

Authentication-Results: "dkim=neutral (bad format) header.i=@example.com"

这很可能是由给定选择器的 DNS TXT 记录拆分成三个或更多资源记录 (RR) 引起的。认证器在连接记录时获得了错误的记录值。

例如,给定选择器 "default._domainkey.example.com" 的 DNS TXT 记录,具有正确、预期的值

"v=DKIM1; k=rsa; s=email; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqrXzI8BMAv3rTYU9FA4F1m2aLyT7JF8qnhTuqWibR/X55ZxoUX8fceXkRbM03tgn+1UWo5mbNN5siLPDlNOKU6fWCmkCbroPXe0vpip72zkFCtYxO4NTQY0kVaKVyFpUbFbxN3oabYTmaty3eE2yQDDAmJeZiVyEE7K7E0vnW9KpiJypFPFoft52Dqr3BTB8197gHPEMXgeP5gYkjJxVEfJZiZVco6p41JUr0CzD2dPun6pSLOO8NCkx3bWNKsL1DA7CR6qX/o2oOsd821N+0tn+8oc6x0rnhetaR0442NAGzxna4jTkUe9jwAK4aU7nKQxqNn/wOw1K2qT7uhsVMwIDAQAB".

另一端的认证器收到拆分成三条的记录,

$ resolver -t TXT default._domainkey.example.com
= options: &{sqtype:TXT sqclass:IN nameserver:udp://127.0.0.1 insecure:false qname:default._domainkey.example.com qtype:16 qclass:1}
= resolv.conf: &{Domain:localhost Search:[] NameServers:[127.0.0.1] NDots:1 Timeout:5 Attempts:2 OptMisc:map[]}
> Lookup default._domainkey.example.com at 127.0.0.1:53
< From: 127.0.0.1:53
> Header: {ID:2136 IsQuery:false Op:0 IsAA:false IsTC:false IsRD:true IsRA:true RCode:0 QDCount:1 ANCount:3 NSCount:0 ARCount:0}
> Question: &{Name:default._domainkey.example.com Type:TXT}
> Status: OK
> Answer #1:
>> Resource record: {Name:default._domainkey.example.com Type:16 Class:1 TTL:1822 rdlen:143}
>> RDATA: YkjJxVEfJZiZVco6p41JUr0CzD2dPun6pSLOO8NCkx3bWNKsL1DA7CR6qX/o2oOsd821N+0tn+8oc6x0rnhetaR0442NAGzxna4jTkUe9jwAK4aU7nKQxqNn/wOw1K2qT7uhsVMwIDAQAB
> Answer #2:
>> Resource record: {Name:default._domainkey.example.com Type:16 Class:1 TTL:1822 rdlen:253}
>> RDATA: p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqrXzI8BMAv3rTYU9FA4F1m2aLyT7JF8qnhTuqWibR/X55ZxoUX8fceXkRbM03tgn+1UWo5mbNN5siLPDlNOKU6fWCmkCbroPXe0vpip72zkFCtYxO4NTQY0kVaKVyFpUbFbxN3oabYTmaty3eE2yQDDAmJeZiVyEE7K7E0vnW9KpiJypFPFoft52Dqr3BTB8197gHPEMXgeP5g
> Answer #3:
>> Resource record: {Name:default._domainkey.example.com Type:16 Class:1 TTL:1822 rdlen:26}
>> RDATA: v=DKIM1; k=rsa; s=email;

当认证器组合记录时,它获得的值是,

YkjJxVEfJZiZVco6p41JUr0CzD2dPun6pSLOO8NCkx3bWNKsL1DA7CR6qX/o2oOsd821N+0tn+8oc6x0rnhetaR0442NAGzxna4jTkUe9jwAK4aU7nKQxqNn/wOw1K2qT7uhsVMwIDAQABp=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqrXzI8BMAv3rTYU9FA4F1m2aLyT7JF8qnhTuqWibR/X55ZxoUX8fceXkRbM03tgn+1UWo5mbNN5siLPDlNOKU6fWCmkCbroPXe0vpip72zkFCtYxO4NTQY0kVaKVyFpUbFbxN3oabYTmaty3eE2yQDDAmJeZiVyEE7K7E0vnW9KpiJypFPFoft52Dqr3BTB8197gHPEMXgeP5gv=DKIM1; k=rsa; s=email;

解决方案: 生成 1024 位长度的密钥(或小于 2048 位),使其适合 DNS TXT 记录中的 255 个字符。

备注

当您准备好对抗垃圾邮件并提高用户对您的服务器的信任时,您可能还想看看 Sender Policy Framework,这基本上意味着添加一个 DNS 记录,说明哪些服务器被授权代表您的域发送电子邮件。

参见

© . This site is unofficial and not affiliated with Arch Linux.

Content is available under GNU Free Documentation License 1.3 or later unless otherwise noted.