在 VPS 上部署 DKIM 进行邮件认证

DKIM (DomainKeys Identified Mail,域名密钥识别邮件)是一种部署在服务器上使用公钥和私钥对电子邮件进行数字签名和验证的方法。启用 DKIM 机制后,服务器发出的邮件就可以被确切地确认来源从而防止别人伪造冒用自己的域名发送电子邮件。这也可以减少所发邮件被识别为垃圾邮件的情况。

OpenDKIM 是 DKIM 的一个开源实现。前些日子把它部署在了水景一页的服务器上。这里是部署过程的记录。期间参考了这两篇英文教程,

如果只有自己一个域名在主机上,照着第 1 篇来就行了,要在同一台服务器上针对多个邮件域名/子域名进行邮件认证需要结合这两篇文章的指导。

注:因时间过去太久,细节记不清楚了,回头再次配置的时候再修改。如果有朋友参考这篇文章的话还请注意。

下面的部署过程包括软件安装假设是在 CentOS 7 系统中进行。使用的邮件服务器程序是 Postfix (MTA)。假设 Postfix 已经安装并针对下面的邮件主机和域名启用:

邮件服务器主机名 Hostname 为 mail.cnzhx.net
域名 Domain 为 cnzhx.net

使用 EPEL 源

如果没有在系统中启用 EPEL 源,需要先启用他。详见这里

安装 OpenDKIM

# yum install -y opendkim

生成密钥对并修改权限

使用下面的指令在系统的 /etc/opendkim/keys 文件夹下创建公钥和私钥。(请注意根据自己的情况调整路径和主机以及域名。)

# mkdir /etc/opendkim/keys/cnzhx.net
# opendkim-genkey -D /etc/opendkim/keys/cnzhx.net/ -d cnzhx.net mail.cnzhx.net -s cnzhx

其中 -d 指定需要使用此密钥的域名,可以有多个,比如上面的 cnzhx.netmail.cnzhx.net-s cnzhx 是指令生成的公钥/私钥文件的选择器(文件名,其实就是个标记),默认(即不使用 -s cnzhx 的情况下)是 default。详见该指令的说明文档。一般情况下可不指定选择器,但是如果有个多个域名分别使用不同的公钥和私钥,那就肯定需要为它们指定不同的选择器了。该字符串将会包含在 DKIM 的签名中。

生成的文件中,default.private(这里是 cnzhx.private)是针对该域名的私钥;default.txt(这里是 cnzhx.txt)里面的文本是公钥。公钥将会通过域名解析系统的 TXT 记录公布到网上。

修改公钥/私钥文件权限使 opendkim 的用户和组可以访问它们,

# chown -R opendkim:opendkim /etc/opendkim/keys

编辑配置文件

然后就需要编辑(如果系统中没有就需要自己创建)下面这些文件:

  • /etc/opendkim.conf — OpenDKIM 的主配置文件
  • /etc/opendkim/KeyTable — 供签名用的密钥(及其路径)列表
  • /etc/opendkim/SigningTable — 允许签名的域名以及账户列表(即如何使用对应的密钥)
  • /etc/opendkim/TrustedHosts — 签名和验证时默认“信任”的服务器列表

注:以下未提到的参数使用默认配置即可。

编辑主配置文件

路径 /etc/opendkim.conf

设置如下参数:

Mode sv
Canonicalization relaxed/simple
UMask 022
# Selector cnzhx # 如果前面指定了 选择器 的话需要取消注释该行并填写正确的选择器。

编辑 KeyTable

路径 /etc/opendkim/KeyTable

替换其中的 example.com 为自己的域名。类似于,

cnzhx._domainkey.cnzhx.net cnzhx.net:cnzhx:/etc/opendkim/keys/cnzhx.net/cnzhx.private
cnzhx._domiankey.mail.cnzhx.net mail.cnzhx.net:cnzhx:/etc/opendkim/keys/cnzhx.net/cnzhx.private

注意选择器的位置使用蓝色字体标出来了。

编辑 SigningTable

路径 /etc/opendkim/SigningTable

这里使用通配符 * 来指明针对使用特定电邮后缀(域名)的所有邮箱地址发送的邮件进行签名认证。

# WILDCARD EXAMPLE
# Enables signing for any address on the listed domain(s), but will work only if
# "refile:/etc/opendkim/SigningTable" is included in /etc/opendkim.conf.
# Create additional lines for additional domains.

*@cnzhx.net cnzhx._domainkey.cnzhx.net
*@cnzhx.net cnzhx._domainkey.cnzhx.net

编辑 TrustedHosts

路径 /etc/opendkim/TrustedHosts

在 localhost IP (127.0.0.1) 下面添加服务器的 FQDN 和域名。

# OPENDKIM TRUSTED HOSTS
# To use this file, uncomment the #ExternalIgnoreList and/or the #InternalHosts
# option in /etc/opendkim.conf then restart OpenDKIM. Additional hosts
# may be added on separate lines (IP addresses, hostnames, or CIDR ranges).
# The localhost IP (127.0.0.1) should always be the first entry in this file.
127.0.0.1
::1
www.cnzhx.net
mail.cnzhx.net
test.cnzhx.net
cnzhx.net
#host.example.com
#192.168.1.0/24

实际上水景一页的 VPS 目前配置成只有一个主域名(也是机器的域名)cnzhx.net 会发送邮件。其它域名和子域名只是托管在该主机上,并没有配置独立的邮件服务。所以实际上上面只填写一个 cnzhx.net 就可以了。

编辑 Postfix 配置文件

路径 /etc/postfix/main.cf

# vi /etc/postfix/main.cf

在文件内容最后添加下面的几行内容:

smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = $smtpd_milters
milter_default_action = accept

(重新)启动 OpenDKIM 和 postfix 服务

# systemctl start opendkim ; systemctl enable opendkim ; systemctl restart postfix

更新域名的 TXT 记录

使用前面生成的 default.txt (或这里的 cnzhx.txt) 文件的内容更新域名的 TXT 记录。这样别的服务器在收到邮件的时候就可以根据域名解析内容中的公钥来验证邮件是否确实由此服务器发出。

登录自己的域名管理服务,在 cnzhx.net 域名下新建一个 TXT 类型的记录。

Name 处填写,

cnzhx._domainkey

或者可能是(根据选择器)

default._domainkey

Value 处填写,

v=DKIM1; k=rsa; p=.........(那一串字符).........

TTL 可以直接取默认值。

发送一封测试邮件并检查日志

从当前 Linux 用户帐户发送(此时发送者为 username@domain.name),

# mail -s "test DKIM" info@cnzhx.net .
# tail -f /var/log/maillog

配置多域名

配置 openDKIM

如果有另一个域名 cnzhxother.foo 也托管在同一个服务器上,并且需要通过自己的域名发送通知邮件(注意此服务器不接收邮件),需要给该域名按照前面的方法添加密钥对、在 SigningTableKeyTable 中增加相应记录,还需要将该域名添加到 TrustedHosts 列表中。

测试从命令行发送邮件

从指定的另一个配置好的域名发送,

echo "test another account for mail sign" | mail -s "test opendkim" -r "other@cnzhxother.foo (Other)" info@cnzhx.net

如果没有错误就配置完成了。同样可以查看日志看是否有 DKIM-Signature field added 字样。

配置域名解析记录

然后还需要相应配置 cnzhxother.foo 的域名记录:

  1. 增加 MX 并将服务器设置为主域名,比如这里的 cnzhx.net,反正不接收邮件,似乎问题不大。这样做的目的是,邮件服务器是有逆向域名解析 PTR 的(我这里设置为主域名了)。同时将子域名设置为 mail 或其它。
  2. 增加 mail (或其它)的 A/AAAA 记录:mail.cnzhxother.foo 到主机 IP 地址。
  3. 将上面生成的 cnzhxother.foo 的公钥 default.txt 的内容添加到域名 cnzhxother.foo 的 TXT 记录中。
  4. 再增加一个 SPF 记录为 DNS 的 TXT 记录,内容如下(假设只通过该服务器发送邮件),
    v=spf1 mx ip4:<服务器 IPv4 地址> ~all
    其中 <服务器 IPv4 地址> 应该替换为实际的 IP 地址,如 56.78.90.12。然后可以用 https://mxtoolbox.com/spf.aspx 来测试一下。

这个地方 https://www.mail-tester.com/spf-dkim-check 可以测试 SPF 和 DKIM 是否已经配置并随 DNS 记录正确传递出去。

但是我配置的这另一个域名发送的邮件没有显示 mailed-by 和 (增加了 MX 之类的之后终于有 mailed-by 了)signed by,虽然也没有显示未加密之类的警告。暂时还不知道为什么邮件发送记录中有 DKIM-Signature 却在接收到的邮件中不显示这个 signed by。(补充)貌似是因为 DNS 解析记录还没有传递到,现在已经可以正确显示 mailed-by 和 signed by 了。

配置 Web 服务器

如果要从网络应用中通过托管的域名(非服务器 FQDN 域名)发送电子邮件,比如从 cnzhxother.foo 对应的 WordPress 中发送邮件通知,则还需要配置相应的 Web 服务器。

以 WordPress 为例,假设其服务器程序是 Apache 2 + php-fpm(水景一页就采用了这种配置方式)。WordPress 是 PHP 应用,需要配置 PHP 参数 sendmail_path。该参数默认情况下是在 /etc/php.ini 中配置的。但是 /etc/php.ini 是全局的,也就是说,服务上所有域名对应的 WordPress 所用的 PHP 解释程序都使用这个配置。所以默认情况下, /etc/php.ini 中设置的默认发送者邮件地址肯定是属于主域名的,比如,

sendmail_path=/usr/sbin/sendmail -t -i -f info@cnzhx.net

那么 cnzhxother.foo 上的 WordPress 发送邮件的时候也会通过 info@cnzhx.net 发送,也就是说其 mailed-by 就是 cnzhx.net 而不是 cnzhxother.foo

为了让 cnzhxother.foo 上的 WordPress 在发送邮件的时候显示的 mailed-by 是 cnzhxother.foo,可以在解释 PHP 的时候修改 PHP 参数 sendmail_path。这里使用了 php-fpm 解析 PHP,所以修改其配置文件。假设 cnzhxother.foo 对应的配置文件为,

/etc/php-fpm.d/cnzhxother.conf

将其中(默认是靠近该文件结尾的位置),

;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f www@my.domain.com

一行取消注释(删除行首的 ;)。

然后修改该行最后的邮件地址为为该域名对应的邮件地址,比如,

php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f info@cnzhxother.foo

保存后重新启动 php-fpm 服务即可。©

本文发表于水景一页。永久链接:<http://cnzhx.net/blog/implementation-of-dkim-on-centos-7/>。转载请保留此信息及相应链接。

3 条关于 “在 VPS 上部署 DKIM 进行邮件认证” 的评论

  1. 引用通告: 加密服务器发出的电子邮件 | 水景一页

  2. 引用通告: 记服务器上一个 HOSTS 配置错误 | 水景一页

  3. 引用通告: 「水景一页」邮件订阅变更 | 水景一页

雁过留声,人过留名

您的电子邮箱地址不会被公开。 必填项已用 * 标注

特别提示:与当前文章主题无关的讨论相关但需要较多讨论求助信息请发布到水景一页讨论区的相应版块,谢谢您的理解与合作!请参考本站互助指南
您可以在评论中使用如下的 HTML 标记来辅助表达: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>