OpenDKIM
OpenDKIM 是 DomainKeys Identified Mail (DKIM) 发件人身份验证系统的开源实现。
包括 Yahoo、Google 和 Outlook.com 在内的大多数常用邮件提供商都支持 DKIM。
概念
基本上,DKIM 对来自服务器的所有消息进行数字签名,以验证消息实际上是从相关域名发送的,而不是伪造或修改的。
- 发件人的邮件服务器使用私钥对外发电子邮件进行签名。
- 当邮件到达时,接收者(或其服务器)从域名的 TXT 记录中读取公钥并验证签名。
这确保了消息是从私钥与域名公钥匹配的服务器发送的。
有关更多信息,请参见 RFC 6376。
安装
配置
签名服务的主要配置文件是 /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
- 套接字地址是在
/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
/etc/opendkim/keys/domain1.com/
。否则会出现错误:“dkim: FAILED, invalid (public key: not available)” 错误消息以及 DKIM 电子邮件测试。但是,多个域名可以使用单个 DKIM 密钥。创建以下两个文件,以告知 opendkim 在哪里可以找到正确的密钥。您可以为所有域名使用相同的密钥,也可以为每个域名生成一个密钥。进行更改以匹配您的设置。根据需要添加更多行。在以下示例中,example1.com
和 example2.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 记录,声明哪些服务器有权为您的域名发送电子邮件。