超过14K的Let’s Encrypt加密SSL证书发给了PayPal钓鱼网站

在过去的2016年中,Let’s Encrypt共发出了15,270个域名或证书身份中包含“PayPal”的SSL证书,其中大约14,766(96.7%)是网络钓鱼网站。钓鱼网站滥用Let’s Encrypt的基础设施,Let’s Encrypt正在改名为“Let’s Phish”!

HTTPS意味着“加密的通信渠道”,而不一定是目的地网站是安全的。回到2015年,Let’s Encrypt在一篇博文中明确表示,它不打算成为互联网的HTTPS看门狗,您将paypal.com与playpal.com混淆的事实不是SSL加密的问题。

安全专家Eric Lawrence认为浏览器厂商也造成了一些责任:例如,Internet Explorer 不主动检查证书撤销,Chrome不关心网站上的内容或证书类型。如果站点已经以正确的方式安装了SSL证书,它将以绿色显示“安全”指示,无论其目的如何。

from:Slashdot

在Apache上安装Let’s Encrypt多域名证书

背景

因为WoSign(沃通)未经授权就为GitHub的域名之一颁发了证书,这促使了一项Mozilla和安全社区合作进行的公开调查,调查发现了许多其他WoSign误解的案例,调查还发现Wosign已经秘密收购StartCom,并且后者已经开始使用Wosign的基础设施、员工、政策、签发系统。

最终,从Chrome 56开始,由WoSign和StartCom在2016年10月21日00:00:00 UTC后颁发的证书不受信任。

Apple Root Certificate Program 2016年10月01日 正式宣布在即将发布的安全更新中对沃通的 “WoSign CA Free SSL Certificate G2” 取消信任,所有已经发布到 CT 的旧证书不受影响。正在采取进一步的措施,将阻止来自WoSign和StartCom根CA的证书。

Mozilla 于10月20日公布了对 沃通CA 的最终处理意见,它不再信任在10月21日之后签发的WoSign和StartCom根CA的证书,从 Firefox 51 起移除对4个沃通根证书的信任。

使用Let’s Encrypt免费证书

去年3月以来,behindgfw.com使用的是startcom的免费证书,因为旧的证书还可以用,所以一直没有更换。now,it’s time。

使用Let’s Encrypt官方推荐使用certbot客户端,因为要支持SNI多域名,在tanteng.me上使用了acme-tiny完成验证签发,看了半天觉得还是比较繁琐。研究最后发现certbot也是可以达到SNI多域名功能。示例如下:

wget https://dl.eff.org/certbot-auto
chmod a+x certbot-auto
./certbot-auto certonly --webroot -w /var/www/behindgfw.com/public_html  -d behindgfw.com -d www.behindgfw.com -w /var/www/behindgfw.org/public_html -d behindgfw.org -d www.behindgfw.org

certonly –webroot选项可以在发证过程中不停止Web服务器,使用本地网络服务器验证,–webroot-path或-w使用包含由您的Web服务器提供的文件的顶级目录(“web根目录”)。

……

Saving debug log to /var/log/letsencrypt/letsencrypt.log

……

Performing the following challenges:
http-01 challenge for behindgfw.com
http-01 challenge for www.behindgfw.com
http-01 challenge for behindgfw.org
http-01 challenge for www.behindgfw.org
Using the webroot path /var/www/behindgfw.org/public_html for all unmatched domains.
Waiting for verification…
Cleaning up challenges
Generating key (2048 bits): /etc/letsencrypt/keys/0000_key-certbot.pem
Creating CSR: /etc/letsencrypt/csr/0000_csr-certbot.pem

IMPORTANT NOTES:
– Congratulations! Your certificate and chain have been saved at
/etc/letsencrypt/live/behindgfw.com/fullchain.pem. Your cert will
expire on 2017-06-13. To obtain a new or tweaked version of this
certificate in the future, simply run certbot-auto again. To
non-interactively renew *all* of your certificates, run
“certbot-auto renew”

……

使用/etc/letsencrypt/live目录下的fullchain.pem、privkey.pem修改Apache设置:

SSLCertificateFile /etc/letsencrypt/live/behindgfw.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/behindgfw.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/behindgfw.com/fullchain.pem

即便如此,你仍然用不了Google

A new approach to China

January 12, 2010

We recognize that this may well mean having to shut down Google.cn, and potentially our offices in China.
The decision to review our business operations in China has been incredibly hard, and we know that it will have potentially far-reaching consequences. We want to make clear that this move was driven by our executives in the United States, without the knowledge or involvement of our employees in China who have worked incredibly hard to make Google.cn the success it is today. We are committed to working responsibly to resolve the very difficult issues raised.

链接

http://mt.sohu.com/20160503/n447361316.shtml
https://www.zhihu.com/question/45126377

HTTP日志格式和返回状态码

昨晚服务器443端口遭到DDOS,截取日志片段

117.114.129.162 - - [20/Apr/2016:22:30:06 +0800] "-" 408 0 "-" "-"
61.177.139.204 - - [20/Apr/2016:22:30:40 +0800] "-" 408 3246 "-" "-"
36.110.120.33 - - [20/Apr/2016:22:30:51 +0800] "-" 408 3246 "-" "-"
122.224.219.102 - - [20/Apr/2016:22:31:18 +0800] "-" 408 3246 "-" "-"
180.102.101.208 - - [20/Apr/2016:22:31:51 +0800] "-" 408 3683 "-" "-"
42.80.224.188 - - [20/Apr/2016:22:33:27 +0800] "-" 408 3949 "-" "-"
36.42.226.43 - - [20/Apr/2016:22:41:18 +0800] "-" 408 3246 "-" "-"
116.238.253.155 - - [20/Apr/2016:22:41:50 +0800] "-" 408 3949 "-" "-"
183.11.147.57 - - [20/Apr/2016:22:42:56 +0800] "-" 408 3246 "-" "-"
116.9.33.177 - - [20/Apr/2016:22:43:15 +0800] "-" 408 3246 "-" "-"
113.207.91.2 - - [20/Apr/2016:22:43:25 +0800] "-" 408 0 "-" "-"
182.201.104.24 - - [20/Apr/2016:22:44:07 +0800] "-" 408 3949 "-" "-"
115.171.145.252 - - [20/Apr/2016:22:44:40 +0800] "-" 408 3949 "-" "-"
27.155.137.29 - - [20/Apr/2016:22:44:51 +0800] "-" 408 0 "-" "-"
218.82.191.15 - - [20/Apr/2016:22:45:14 +0800] "-" 408 3246 "-" "-"
183.14.124.8 - - [20/Apr/2016:22:45:43 +0800] "-" 408 3246 "-" "-"
183.37.39.216 - - [20/Apr/2016:22:46:02 +0800] "-" 408 0 "-" "-"
60.168.5.75 - - [20/Apr/2016:22:46:35 +0800] "-" 408 0 "-" "-"
118.26.243.91 - - [20/Apr/2016:22:46:41 +0800] "-" 408 3246 "-" "-"
125.84.63.167 - - [20/Apr/2016:22:46:45 +0800] "-" 408 0 "-" "-"
110.90.114.177 - - [20/Apr/2016:22:46:59 +0800] "-" 408 3246 "-" "-"
123.55.201.98 - - [20/Apr/2016:22:47:07 +0800] "-" 408 3246 "-" "-"
221.136.15.118 - - [20/Apr/2016:22:49:55 +0800] "-" 408 3246 "-" "-"
61.172.240.227 - - [20/Apr/2016:22:52:27 +0800] "-" 408 3246 "-" "-"
123.166.39.206 - - [20/Apr/2016:22:52:55 +0800] "-" 408 3246 "-" "-"
42.81.66.19 - - [20/Apr/2016:22:52:56 +0800] "-" 408 3246 "-" "-"
218.22.27.202 - - [20/Apr/2016:22:53:53 +0800] "-" 408 3246 "-" "-"
110.153.253.202 - - [20/Apr/2016:22:54:17 +0800] "-" 408 3246 "-" "-"
115.228.70.66 - - [20/Apr/2016:22:54:50 +0800] "-" 408 3246 "-" "-"
113.111.82.235 - - [20/Apr/2016:22:55:04 +0800] "-" 408 3246 "-" "-"
222.69.213.227 - - [20/Apr/2016:22:55:31 +0800] "-" 408 3246 "-" "-"
202.195.129.247 - - [20/Apr/2016:22:55:36 +0800] "-" 408 0 "-" "-"
183.63.97.101 - - [20/Apr/2016:22:56:14 +0800] "-" 408 3246 "-" "-"
114.91.69.49 - - [20/Apr/2016:22:56:23 +0800] "-" 408 3246 "-" "-"
222.212.28.92 - - [20/Apr/2016:22:56:45 +0800] "-" 408 3246 "-" "-"
183.39.239.56 - - [20/Apr/2016:22:57:44 +0800] "-" 408 3248 "-" "-"
101.81.228.140 - - [20/Apr/2016:22:58:01 +0800] "-" 408 0 "-" "-"
111.161.31.54 - - [20/Apr/2016:22:59:05 +0800] "-" 408 3246 "-" "-"
218.94.142.92 - - [20/Apr/2016:22:59:16 +0800] "-" 408 3246 "-" "-"

对照服务器上combined格式日志定义”%h %l %u %t \”%r\” %>s %O \”%{Referer}i\” \”%{User-Agent}i\””

格式字符串	描述
%%	百分号(Apache2.0.44或更高的版本)
%a	远端IP地址
%A	本机IP地址
%B	除HTTP头以外传送的字节数
%b	以CLF格式显示的除HTTP头以外传送的字节数,也就是当没有字节传送时显示'-'而不是0。
%{Foobar}C	在请求中传送给服务端的cookieFoobar的内容。
%D	服务器处理本请求所用时间,以微为单位。
%{FOOBAR}e	环境变量FOOBAR的值
%f	文件名
%h	远端主机
%H	请求使用的协议
%{Foobar}i	发送到服务器的请求头Foobar:的内容。
%l	远端登录名(由identd而来,如果支持的话),除非IdentityCheck设为"On",否则将得到一个"-"。
%m	请求的方法
%{Foobar}n	来自另一个模块的注解Foobar的内容。
%{Foobar}o	应答头Foobar:的内容。
%p	服务器服务于该请求的标准端口。
%P	为本请求提供服务的子进程的PID。
%{format}P	服务于该请求的PID或TID(线程ID),format的取值范围为:pid和tid(2.0.46及以后版本)以及hextid(需要APR1.2.0及以上版本)
%q	查询字符串(若存在则由一个"?"引导,否则返回空串)
%r	请求的第一行
%s	状态。对于内部重定向的请求,这个状态指的是原始请求的状态,---%>s则指的是最后请求的状态。
%t	时间,用普通日志时间格式(标准英语格式)
%{format}t	时间,用strftime(3)指定的格式表示的时间。(默认情况下按本地化格式)
%T	处理完请求所花时间,以秒为单位。
%u	远程用户名(根据验证信息而来;如果返回status(%s)为401,可能是假的)
%U	请求的URL路径,不包含查询字符串。
%v	对该请求提供服务的标准ServerName。
%V	根据UseCanonicalName指令设定的服务器名称。
%X	请求完成时的连接状态:
X= 连接在应答完成前中断。 += 应答传送完后继续保持连接。 -= 应答传送完后关闭连接。 (在1.3以后的版本中,这个指令是%c,但这样就和过去的SSL语法:%{var}c冲突了)
%I 接收的字节数,包括请求头的数据,并且不能为零。要使用这个指令你必须启用mod_logio模块。 %O 发送的字节数,包括请求头的数据,并且不能为零。要使用这个指令你必须启用mod_logio模块。

再顺便学习下Http 状态码:

http 状态码基本上可以分为 5 类:

1xx 为消息类,该类状态代码用于表示服务器临时回应。

100 Continue 表示初始的请求已经被服务器接受,浏览器应当继续发送请求的其余部分(HTTP 1.1)

101 Switching Protocols 服务器将遵从客户的请求转换到另外一种协议(HTTP 1.1)。

2xx 表示浏览器端请求被处理成功。

200 OK 一切正常。

201 Created 服务器已经创建了文档,Location 头给出了它的 URL。

202 Accepted 已经接受请求,但处理尚未完成。

203 Non-Authoritative Information 文档已经正常地返回,但一些应答头可能不正确,因为使用的是文档的拷贝(HTTP 1.1新)。

204 No Content 没有新文档,浏览器应该继续显示原来的文档。这个跟下面的 304 非常相似。

205 Reset Content 没有新的内容,但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容(HTTP 1.1新)。

206 Partial Content 客户发送了一个带有 Range 头的GET请求,服务器完成了它(HTTP 1.1新)。注意,通过 Range 可以实现断点续传。

3xx 重定向。

300 Multiple Choices 客户请求的文档可以在多个位置找到,这些位置已经在返回的文档内列出。如果服务器要提出优先选择,则应该在Location应答头指明。

301 Moved Permanently 客户请求的文档在其他地方,新的URL在Location头中给出,浏览器应该自动地访问新的URL。

302 Found 类似于301,但新的URL应该被视为临时性的替代,而不是永久性的。注意,在HTTP1.0中对应的状态信息是“Moved Temporatily”。
出现该状态代码时,浏览器能够自动访问新的URL,因此它是一个很有用的状态代码。
注意这个状态代码有时候可以和301替换使用。例如,如果浏览器错误地请求http://host/~user (缺少了后面的斜杠),有的服务器返回301,有的则返回302。
严格地说,我们只能假定只有当原来的请求是GET时浏览器才会自动重定向。请参见307。

303 See Other 类似于301/302,不同之处在于,如果原来的请求是POST,Location头指定的重定向目标文档应该通过GET提取(HTTP 1.1新)。

304 Not Modified 客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。

305 Use Proxy 客户请求的文档应该通过Location头所指明的代理服务器提取(HTTP 1.1新)。

307 Temporary Redirect 和302(Found)相同。许多浏览器会错误地响应302应答进行重定向,即使原来的请求是POST,即使它实际上只能在POST请求的应答是303时 才能重定向。由于这个原因,HTTP 1.1新增了307,以便更加清除地区分几个状态代码:当出现303应答时,浏览器可以跟随重定向的GET和POST请求;如果是307应答,则浏览器只 能跟随对GET请求的重定向。(HTTP 1.1新)

4xx 错误

400 Bad Request 请求出现语法错误。

401 Unauthorized 客户试图未经授权访问受密码保护的页面。应答中会包含一个WWW-Authenticate头,浏览器据此显示用户名字/密码对话框,然后在填写合适的Authorization头后再次发出请求。

403 Forbidden 资源不可用。服务器理解客户的请求,但拒绝处理它。通常由于服务器上文件或目录的权限设置导致。

404 Not Found 无法找到指定位置的资源。这也是一个常用的应答。

405 Method Not Allowed 请求方法(GET、POST、HEAD、Delete、PUT、TRACE等)对指定的资源不适用。(HTTP 1.1新)

406 Not Acceptable 指定的资源已经找到,但它的MIME类型和客户在Accpet头中所指定的不兼容(HTTP 1.1新)。

407 Proxy Authentication Required 类似于401,表示客户必须先经过代理服务器的授权。(HTTP 1.1新)

408 Request Timeout 在服务器许可的等待时间内,客户一直没有发出任何请求。客户可以在以后重复同一请求。(HTTP 1.1新)

409 Conflict 通常和PUT请求有关。由于请求和资源的当前状态相冲突,因此请求不能成功。(HTTP 1.1新)

410 Gone 所请求的文档已经不再可用,而且服务器不知道应该重定向到哪一个地址。它和404的不同在于,返回407表示文档永久地离开了指定的位置,而404表示由于未知的原因文档不可用。(HTTP 1.1新)

411 Length Required 服务器不能处理请求,除非客户发送一个Content-Length头。(HTTP 1.1新)

412 Precondition Failed 请求头中指定的一些前提条件失败(HTTP 1.1新)。

413 Request Entity Too Large 目标文档的大小超过服务器当前愿意处理的大小。如果服务器认为自己能够稍后再处理该请求,则应该提供一个Retry-After头(HTTP 1.1新)。

414 Request URI Too Long URI太长(HTTP 1.1新)。

416 Requested Range Not Satisfiable 服务器不能满足客户在请求中指定的Range头。(HTTP 1.1新)

5xx 服务器错误

500 Internal Server Error 服务器遇到了意料不到的情况,不能完成客户的请求。

501 Not Implemented 服务器不支持实现请求所需要的功能。例如,客户发出了一个服务器不支持的PUT请求。

502 Bad Gateway 服务器作为网关或者代理时,为了完成请求访问下一个服务器,但该服务器返回了非法的应答。

503 Service Unavailable 服务器由于维护或者负载过重未能应答。例如,Servlet可能在数据库连接池已满的情况下返回503。服务器返回503时可以提供一个Retry-After头。

504 Gateway Timeout 由作为代理或网关的服务器使用,表示不能及时地从远程服务器获得应答。(HTTP 1.1新)

505 HTTP Version Not Supported 服务器不支持请求中所指明的HTTP版本。(HTTP 1.1新)

 

值得注意的是“%r”请求的第一行没有内容,也就是空请求,也难怪返回408状态码了。

https和http根文档目录不同时,如何设置用https访问WordPress后台

标题好长,一口气喘不过来。

WordPress后台设置https登录,貌似安全很多,通过在wp-config.php添加以下代码实现。

define(‘FORCE_SSL_ADMIN’,true);

在WordPress官方文档也提到在http和https两个虚拟主机里分别设置一个地址相同的链接:

Sometimes, you want your whole wp-admin to run over a secure connection using the https protocol. Conceptually, the procedure works like this:

Set up two virtual hosts with the same url (the blog url), one secure, the other not.
On the secure virtual host, set up a rewrite rule that shuttles all non-wp-admin traffic to the insecure site.
On the insecure virtual host, set up a rewrite rule that shuttles all traffic to wp-admin to the secure host.
Put in a filter (via a plugin) that filters the links in wp-admin so that once activated, administrative links are rewritten to use https and that edits cookies to work only over encrypted connections.

不想开启全站https,同时http和https在不同目录时,用rewrite转来转去实在麻烦。

其实通过Apache别名可以简单搞定,在https虚拟主机里写入如下代码:

Alias /blogurl /pathto/http/blogurl

这样,两个虚拟主机使用的是同一个目录,还可以避免插件、主题、上传文档各种不同步问题。
前面的/blogurl指的是https WordPress目录,这是一个虚拟目录,物理上不存在的。后面的/blogurl代表物理存在的http WordPress目录

不知道还有什么没发现的问题?

参考:https://codex.wordpress.org/Administration_Over_SSL

部署DMARC

前面转发了一篇SPF、DKIM设置的英文文章,今天再写点关于节译篇邮件DMARC协议的文章。

背景

电子邮件认证技术SPF和DKIM为邮件发送者提供身份证明,这些技术的应用一直在稳步增加,但欺诈和欺骗性电子邮件的问题并未减轻。看起来好像如果发件人坚持使用这些技术,那么MTA将能够轻松地区分那些诈骗信。不幸的是,一些特殊的原因使它失效了。
1、域内多名发送者带来一个复杂的电子邮件环境,使用不同的系统发送电子邮件,而且通常还包括第三方服务商。确保每封邮件可以使用SPF或DKIM是一项复杂的任务,特别是考虑到这些环境还在不断变化的状态。
2、如果一个域名持有人外发邮件中,其中一些被签名,另外一些没有,那么电子邮件接收器将被迫从一堆欺诈邮件中分辨出合法邮件。垃圾邮件算法容易出错,需要不断发展,以适应垃圾邮件发送者改变策略的回应。其结果是,一些诈骗短信将不可避免地进入用户的收件箱。
3、发件人得到的反馈信息很少。除非邮件退回给发件人,没有办法确定有多少以伪造发件人域的欺诈性电子邮件被发送。
4、即使发送者搞定了他们的邮件系统,对所有的合法邮件进行身份验证,电子邮件接收器仍持谨慎态度拒绝未经身份验证的消息,因为他们不能确定有没有合法邮件是未签名的。

这些问题可以得到解决的唯一办法是,让发送者和接收者互相共享信息,发件人要告诉接收者在收到未验证的邮件该怎么办。

幸运的是Paypal开创并摸索出一套系统DMARC,雅虎邮件和Gmail以这种方式进行合作。结果是非常有效的,从而导致欺诈电子邮件显著下降。

DMARC的目标是建立发送者和接收者的合作,以加强发件人的邮件验证的做法,使接收器拒绝未经证实的消息。

DMARC和电子邮件验证过程

DMARC设计以适应入站电子邮件身份验证过程,一个接收器部署SPF和DKIM,再加上自己的垃圾邮件过滤器,流程可能会是这个样子:

在上面的流程中,DMARC在原来ADSP测试点进行检测。所有其他测试不受影响。

DMARC设计成满足以下要求:

最大限度地减少误报,提供强大的认证报告。减少网络钓鱼,最小化的复杂性。

需要注意的是DMARC建立在域名密钥识别邮件(DKIM)和发件人策略框架(SPF)上,DMARC被设计旨在更换ADSP,并增加对以下功能的支持:

泛域名和子域名策略,
不存在的子域,
慢部署(例如百分测试),
SPF,
隔离邮件。

发布DNS txt纪录

DMARC发布在DNS文本(TXT)资源记录(RR)中,并告诉收件方收到伪造邮件后做什么。

举一个例子,下面为域“Behindgfw.com”的DNS DMARC txt记录:

“v=DMARC1;p=reject;pct=100;rua=mailto:info@Behindgfw.com”

在本实施例中,发送者的请求,接收器直接拒绝所有的伪造邮件。

p:用于告知收件方,当检测到某邮件存在伪造发件人的情况,收件方要做出什么处理,处理方式从轻到重依次为:none为不作任何处理;quarantine为将邮件标记为垃圾邮件;reject为拒绝该邮件。初期建议设置为none。

rua:用于在收件方检测后,将一段时间的汇总报告,发送到哪个邮箱地址。

ruf:用于当检测到伪造邮件时,收件方须将该伪造信息的报告发送到哪个邮箱地址。

Pct:检测过滤百分比。

如何部署DMARC的步骤

DMARC是基于一些世界上最大的电子邮件发送者和接收者部署SPF和DKIM的实际经验而设计的。考虑到一个事实,即它几乎是不可能像打开开关一样马上就部署好。

1、部署DKIM和SPF。必须的基础。
2、发布DMARC记录p=none,要求接收方发送检测报告。
3、随着你经验丰富,修改DMARC政策的p标志从none到quarantine到reject。

updata:

1、可以参见google apps 文档 添加 DMARC 记录

2、txt记录的主机字段应该设置为“_dmarc”。

3、设置p=reject后,伪造邮件发送不成功并返回

当前时间:2016-05-10 23:36:50</br>
mx记录解析成功:74.125.25.26
连接到:74.125.25.26成功!
5.7.1 Unauthenticated email from behindgfw.com is not accepted due to 5.7.1 domain’s DMARC policy. Please contact administrator of behindgfw.com 5.7.1 domain if this was a legitimate mail. Please visit 5.7.1 https://support.google.com/mail/answer/2451690 to learn about DMARC 5.7.1 initiative. dr13si3342340pac.245 – gsmtp

参考工具

1、给check-auth@verifier.port25.com发一封邮件,检查spf、dkim部署是否成功。没有部署客户端可以用

“mail -s SUBJECT check-auth@verifier.port25.com”

2、在http://www.mail-tester.com/页面中间会看见一个随机邮件地址,给这个地址发一封邮件,随后点击查看你的邮件得分,会检测spf、dkim、dmarc、以及垃圾邮件等等,最后会给你一个邮件系统得分。

3、http://kitterman.com/dmarc/assistant.html DMARC记录生成工具。

4、DMARC以及图形化的SPF检查工具


2017-05-28 12:36:09 http://www.behindgfw.com/archives/ was crawled by CCBot/2.0 (http://commoncrawl.org/faq/) @ 54.80.39.89:34828

 

Configure SPF and DKIM in Postfix on Ubuntu 14.04.4

SPF (Sender Policy Framework) is a system that identifies to mail servers what hosts are allowed to send email for a given domain. Setting up SPF helps to prevent your email from being classified as spam.

DKIM (DomainKeys Identified Mail) is a system that lets your official mail servers add a signature to headers of outgoing email and identifies your domain’s public key so other mail servers can verify the signature. As with SPF, DKIM helps keep your mail from being considered spam. It also lets mail servers detect when your mail hass been tampered with in transit.

The instructions for setting up DNS for SPF and DKIM are generic. The instructions for configuring the SPF policy agent and OpenDKIM into Postfix should work on any distribution after making respective code adjustments for the package tool and to identify the exact path to OpenDKIM’s socket file in Postfix.

The steps required in this guide require root privileges. Be sure to run the steps below as root or with the sudo prefix.

You must already have Postfix installed, configured and working. Refer to the Linode Postfix Guides for assistance.

sudo apt-get install libsasl2-modules
sudo apt-get install postfix

Publishing an SPF DNS record without having the SPF policy agent configured within Postfix is safe; however, publishing DKIM DNS records without having OpenDKIM working correctly within Postfix can result in your email being discarded by the recipient’s email server.

Install DKIM, SPF and Postfix

  1. Install the three required packages:
    1
    sudo apt-get install opendkim opendkim-tools postfix-policyd-spf-python
    
  2. Add user postfix to the opendkim group so that Postfix can access OpenDKIM’s socket when it needs to:
    1
    sudo adduser postfix opendkim
    

Set up SPF

Add SPF records to DNS

The value in an SPF DNS record will look something like the following examples. The full syntax is at the SPF record syntax page.

Example 1 Allow mail from all hosts listed in the MX records for the domain:

1
    v=spf1 mx -all

Example 2 Allow mail from a specific host:

1
    v=spf1 a:mail.example.com -all
  • The v=spf1 tag is required and has to be the first tag.
  • The last tag, -all, indicates that mail from your domain should only come from servers identified in the SPF string. Anything coming from any other source is forging your domain. An alternative is ~all, indicating the same thing but also indicating that mail servers should accept the message and flag it as forged instead of rejecting it outright. -all makes it harder for spammers to forge your domain successfully; it is the recommended setting. ~all reduces the chances of email getting lost because an incorrect mail server was used to send mail. ~all can be used if you don’t want to take chances.

The tags between identify eligible servers from which email to your domain can originate.

  • mx is a shorthand for all the hosts listed in MX records for your domain. If you’ve got a solitary mail server, mx is probably the best option. If you’ve got a backup mail server (a second MX record), using mx won’t cause any problems. Your backup mail server will be identified as an authorized source for email althought it will probably never send any.
  • The a tag lets you identify a specific host by name or IP address, letting you specify which hosts are authorized. You’d use a if you wanted to prevent the backup mail server from sending outgoing mail or if you wanted to identify hosts other than your own mail server that could send mail from your domain (e.g., putting your ISP’s outgoing mail servers in the list so they’d be recognized when you had to send mail through them).

For now, we’re going to stick with the mx version. It’s simpler and correct for most basic configurations, including those that handle multiple domains. To add the record, go to your DNS management interface and add a record of type TXT for your domain itself (i.e., a blank hostname) containing this string:

1
v=spf1 mx -all

Go to the domain zone page for the selected domain and add a new TXT record. The screen will look something like this once you’ve got it filled out:

Linode DNS manager add TXT record

If your DNS provider allows it (DNS Manager doesn’t), you should also add a record of type SPF, filling it in the same way as you did the TXT record.

 For example freedns.afraid.org requires the values to be written in the style found in BIND zonefiles. Thus, the above SPF record’s value would need to be wrapped in double-quotes like this: "v=spf1 mx -all". You’ll need to consult your DNS provider’s documentation for the exact style required.

Add the SPF policy agent to Postfix

The Python SPF policy agent adds SPF policy-checking to Postfix. The SPF record for the sender’s domain for incoming mail will be checked and, if it exists, mail will be handled accordingly. Perl has its own version, but it lacks the full capabilities of Python policy agent.

  1. If you are using SpamAssassin to filter spam, you may want to edit /etc/postfix-policyd-spf-python/policyd-spf.conf to change the HELO_reject and Mail_From_rejectsettings to False. This edit will cause the SPF policy agent to run its tests and add a message header with the results in it while not rejecting any messages. You may also want to make this change if you want to see the results of the checks but not actually apply them to mail processing. Otherwise, just go with the standard settings.
  2. Edit /etc/postfix/master.cf and add the following entry at the end:
    /etc/postfix/master.cf
    1
    2
    policyd-spf  unix  -       n       n       -       0       spawn
        user=policyd-spf argv=/usr/bin/policyd-spf
    
  3. Open /etc/postfix/main.cf and add this entry to increase the Postfix policy agent timeout, which will prevent Postfix from aborting the agent if transactions run a bit slowly:
    /etc/postfix/main.cf
    1
    policyd-spf_time_limit = 3600
  4. Edit the smtpd_recipient_restrictions entry to add a check_policy_service entry:
    /etc/postfix/main.cf
    1
    2
    3
    4
    5
    smtpd_recipient_restrictions =
        ...
        reject_unauth_destination,
        check_policy_service unix:private/policyd-spf,
        ...
    

    Make sure to add the check_policy_service entry after the reject_unauth_destination entry to avoid having your system become an open relay. If reject_unauth_destination is the last item in your restrictions list, add the comma after it and omit the comma at the end of the check_policy_serviceitem above.

  5. Restart Postfix:
    1
    sudo service postfix restart 
    

You can check the operation of the policy agent by looking at raw headers on incoming email messages for the SPF results header. The header the policy agent adds to messages should look something like this:

1
This header indicates a successful check against the SPF policy of the sending domain. If you changed the policy agent settings in Step 1 to not reject mail that fails the SPF check, you may see Fail results in this header. You won’t see this header on outgoing or local mail.

The SPF policy agent also logs to /var/log/mail.log. In the mail.log file you’ll see messages like this from the policy agent:

1
2
Jan 7 06:24:44 arachnae policyd-spf[21065]: None; identity=helo; client-ip=127.0.0.1; helo=mail.example.com; envelope-from=test@example.com; receiver=tknarr@silverglass.org
Jan 7 06:24:44 arachnae policyd-spf[21065]: Pass; identity=mailfrom; client-ip=127.0.0.1; helo=mail.example.com; envelope-from=test@example.com; receiver=tknarr@silverglass.org

The first message is a check of the HELO command, in this case indicating that there wasn’t any SPF information matching the HELO (which is perfectly OK). The second message is the check against the envelope From address, and indicates the address passed the check and is coming from one of the outgoing mail servers the sender’s domain has said should be sending mail for that domain. There may be other statuses in the first field after the colon indicating failure, temporary or permanent errors and so on.

Set up DKIM

DKIM involves setting up the OpenDKIM package, hooking it into Postfix, and adding DNS records.

Configure OpenDKIM

  1. The main OpenDKIM configuration file /etc/opendkim.conf needs to look like this:
    /etc/opendkim.conf
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    # This is a basic configuration that can easily be adapted to suit a standard
    # installation. For more advanced options, see opendkim.conf(5) and/or
    # /usr/share/doc/opendkim/examples/opendkim.conf.sample.
    
    # Log to syslog
    Syslog          yes
    # Required to use local socket with MTAs that access the socket as a non-
    # privileged user (e.g. Postfix)
    UMask           002
    # OpenDKIM user
    # Remember to add user postfix to group opendkim
    UserID          opendkim
    
    # Map domains in From addresses to keys used to sign messages
    KeyTable        /etc/opendkim/key.table
    SigningTable        refile:/etc/opendkim/signing.table
    
    # Hosts to ignore when verifying signatures
    ExternalIgnoreList  /etc/opendkim/trusted.hosts
    InternalHosts       /etc/opendkim/trusted.hosts
    
    # Commonly-used options; the commented-out versions show the defaults.
    Canonicalization    relaxed/simple
    Mode            sv
    SubDomains      no
    #ADSPAction     continue
    AutoRestart     yes
    AutoRestartRate     10/1M
    Background      yes
    DNSTimeout      5
    SignatureAlgorithm  rsa-sha256
    
    # Always oversign From (sign using actual From and a null From to prevent
    # malicious signatures header fields (From and/or others) between the signer
    # and the verifier.  From is oversigned by default in the Debian pacakge
    # because it is often the identity key used by reputation systems and thus
    # somewhat security sensitive.
    OversignHeaders     From

    Edit /etc/opendkim.conf and replace it’s contents with the above, or download a copy of opendkim.conf, upload it to your server and copy it over /etc/opendkim.conf.

  2. Ensure that file permissions are set correctly:
    1
    chmod u=rw,go=r /etc/opendkim.conf
    
  3. Create the directories to hold OpenDKIM’s data files, assign ownership to the opendkim user, and restrict the file permissions:
    1
    2
    3
    4
    mkdir /etc/opendkim
    mkdir /etc/opendkim/keys
    chown -R opendkim:opendkim /etc/opendkim
    chmod go-rw /etc/opendkim/keys
    
  4. Create the signing table /etc/opendkim/signing.table. It needs to have one line per domain that you handle email for. Each line should look like this:
    /etc/opendkim/signing.table
    1
    *@example.com   key
    

    Replace example.com with your domain and key with a short name for the domain. The first field is a pattern that matches e-mail addresses. The second field is a name for the key table entry that should be used to sign mail from that address. For simplicity’s sake, we’re going to set up one key for all addresses in a domain.

  5. Create the key table /etc/opendkim/key.table. It needs to have one line per short domain name in the signing table. Each line should look like this:
    /etc/opendkim/key.table
    1
    key     example.com:YYYYMM:/etc/opendkim/keys/key.private
    

    Replace key with the key value you used for the domain in the signing table (make sure to catch the second occurrence at the end, where it’s followed by .private). Replace example.comwith your domain name and replace the YYYYMM with the current 4-digit year and 2-digit month (this is referred to as the selector). The first field connects the signing and key tables.

    The second field is broken down into 3 sections separated by colons.

    • The first section is the domain name for which the key is used.
    • The second section is a selector used when looking up key records in DNS.
    • The third section names the file containing the signing key for the domain.

    The flow for DKIM lookup starts with the sender’s address. The signing table is scanned until an entry whose pattern (first item) matches the address is found. Then, the second item’s value is used to locate the entry in the key table whose key information will be used. For incoming mail the domain and selector are then used to find the public key TXT record in DNS and that public key is used to validate the signature. For outgoing mail the private key is read from the named file and used to generate the signature on the message.

  6. Create the trusted hosts file /etc/opendkim/trusted.hosts. Its contents need to be:
    /etc/opendkim/trusted.hosts
    1
    2
    3
    4
    5
    6
    127.0.0.1
    ::1
    localhost
    myhostname
    myhostname.example.com
    example.com
    

    When creating the file, change myhostname to the name of your server and replace example.comwith your own domain name. We’re identifying the hosts that users will be submitting mail through and should have outgoing mail signed, which for basic configurations will be your own mail server.

  7. Make sure the ownership and permissions on /etc/opendkim and it’s contents are correct (opendkim should own everything, the keys directory should only be accessible by the owner) by running the following commands:
    1
    2
    chown -R opendkim:opendkim /etc/opendkim
    chmod -R go-rwx /etc/opendkim/keys
    
  8. Generate keys for each domain:
    1
    opendkim-genkey -b 2048 -r -s YYYYMM
    

    Replace YYYYMM with the current year and month as in the key table. This will give you two files,YYYYMM.private containing the key and YYYYMM.txt containing the TXT record you’ll need to set up DNS. Rename the files so they have names matching the third section of the second field of the key table for the domain:

    1
    2
    mv YYYYMM.private key.private
    mv YYYYMM.txt example.txt
    

    Repeat the commands in this step for every entry in the key table. The -b 2048 indicates the number of bits in the RSA key pair used for signing and verification. 1024 bits is the minimum, but with modern hardware 2048 bits is safer. (It’s possible 4096 bits will be required at some point.)

  9. Make sure the ownership, permissions and contents on /etc/opendkim are correct by running the following commands:
    1
    2
    3
    cd /etc
    chown -R opendkim:opendkim /etc/opendkim
    chmod -R go-rw /etc/opendkim/keys
    
  10. Check that OpenDKIM starts correctly:
    1
    sudo service opendkim restart

    You should not get error messages, but if you do, use:

    1
    service opendkim status
    

    to get the status and untruncated error messages.

Set up DNS

As with SPF, DKIM uses TXT records to hold information about the signing key for each domain. Using YYYYMM as above, you need to make a TXT record for the host YYYYMM._domainkey for each domain you handle mail for. Its value can be found in the example.txt file for the domain. Those files look like this:

example.txt
201510._domainkey  IN  TXT ( "**v=DKIM1; k=rsa; s=email; "
    "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu5oIUrFDWZK7F4thFxpZa2or6jBEX3cSL6b2TJdPkO5iNn9vHNXhNX31nOefN8FksX94YbLJ8NHcFPbaZTW8R2HthYxRaCyqodxlLHibg8aHdfa+bxKeiI/xABRuAM0WG0JEDSyakMFqIO40ghj/h7DUc/4OXNdeQhrKDTlgf2bd+FjpJ3bNAFcMYa3Oeju33b2Tp+PdtqIwXR"
    "ZksfuXh7m30kuyavp3Uaso145DRBaJZA55lNxmHWMgMjO+YjNeuR6j4oQqyGwzPaVcSdOG8Js2mXt+J3Hr+nNmJGxZUUW4Uw5ws08wT9opRgSpn+ThX2d1AgQePpGrWOamC3PdcwIDAQAB**" )  ; ----- DKIM key 201510 for example.com

The value inside the parentheses is what you want. Select and copy the entire region from (but not including) the double-quote before v=DKIM1 on up to (but not including) the final double-quote before the closing parentheses. Then edit out the double-quotes within the copied text and the whitespace between them. From the above file the result would be:

example-copied.txt
v=DKIM1; k=rsa; s=email; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu5oIUrFDWZK7F4thFxpZa2or6jBEX3cSL6b2TJdPkO5iNn9vHNXhNX31nOefN8FksX94YbLJ8NHcFPbaZTW8R2HthYxRaCyqodxlLHibg8aHdfa+bxKeiI/xABRuAM0WG0JEDSyakMFqIO40ghj/h7DUc/4OXNdeQhrKDTlgf2bd+FjpJ3bNAFcMYa3Oeju33b2Tp+PdtqIwXRZksfuXh7m30kuyavp3Uaso145DRBaJZA55lNxmHWMgMjO+YjNeuR6j4oQqyGwzPaVcSdOG8Js2mXt+J3Hr+nNmJGxZUUW4Uw5ws08wT9opRgSpn+ThX2d1AgQePpGrWOamC3PdcwIDAQAB

Paste that into the value for the TXT record.

This is what the add TXT record screen will look like when you have it filled out:

Linode DNS manager add TXT record

Repeat this for every domain you handle mail for, using the .txt file for that domain.

Test your configuration

Test the keys for correct signing and verification using the opendkim-testkey command:

1
opendkim-testkey -d example.com -s YYYYMM

If everything is OK you shouldn’t get any output. If you want to see more information, add -vvv to the end of the command. That produces verbose debugging output. The last message should be “key OK”. Just before that you may see a “key not secure” message. That’s normal and doesn’t signal an error, it just means your domain isn’t set up for DNSSEC yet.

Hook OpenDKIM into Postfix

  1. Create the OpenDKIM socket directory in Postfix’s work area and make sure it has the correct ownership:
    1
    2
    mkdir /var/spool/postfix/opendkim
    chown opendkim:postfix /var/spool/postfix/opendkim
    
  2. Set the correct socket for Postfix in the OpenDKIM defaults file /etc/defaults/opendkim:
    /etc/defaults/opendkim
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    # Command-line options specified here will override the contents of
    # /etc/opendkim.conf. See opendkim(8) for a complete list of options.
    #DAEMON_OPTS=""
    #
    # Uncomment to specify an alternate socket
    # Note that setting this will override any Socket value in opendkim.conf
    SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"
    #SOCKET="inet:54321" # listen on all interfaces on port 54321
    #SOCKET="inet:12345@localhost" # listen on loopback on port 12345
    #SOCKET="inet:12345@192.0.2.1" # listen on 192.0.2.1 on port 12345
    

    Uncomment the first SOCKET line and edit it so it matches the uncommented line in the above file. The path to the socket is different from the default because on Debian 8 the Postfix process that handles mail runs in a chroot jail and can’t access the normal location.

  3. Edit /etc/postfix/main.cf and add a section to activate processing of e-mail through the OpenDKIM daemon:
    /etc/postfix/main.cf
    1
    2
    3
    4
    5
    6
    # Milter configuration
    # OpenDKIM
    milter_default_action = accept
    milter_protocol = 2
    smtpd_milters = local:/opendkim/opendkim.sock
    non_smtpd_milters = local:/opendkim/opendkim.sock

    You can put this anywhere in the file. The usual practice is to put it after thesmtpd_recipient_restrictions entry. You’ll notice the path to the socket isn’t the same here as it was in the /etc/defaults/opendkim file. That’s because of Postfix’s chroot jail, the path here is the path within that restricted view of the filesystem instead of within the actual filesystem.

  4. Restart the OpenDKIM daemon so it sets up the correct socket for Postfix:
    1
    sudo service opendkim restart
    
  5. Restart Postfix so it starts using OpenDKIM when processing mail:
    1
    sudo service postfix restart

Verify that everything’s fully operational

The easiest way to verify that everything’s working is to send a test e-mail tocheck-auth@verifier.port25.com using an email client configured to submit mail to the submission port on your mail server. It will analyze your message and mail you a report indicating whether your email was signed correctly or not. It also reports on a number of other things such as SPF configuration and SpamAssassin flagging of your domain. If there’s a problem, it’ll report what the problem was.

Set up Author Domain Signing Practices (ADSP) (optional)

As a final item, you can add an ADSP policy to your domain saying that all emails from your domain should be DKIM-signed. As usual, it’s done with a TXT record for host _adsp._domainkey in your domain with a value of dkim=all.The screen for the new text record will look like this:

Linode DNS manager add TXT record

You don’t need to set this up, but doing so makes it harder for anyone to forge email from your domains because recipient mail servers will see the lack of a DKIM signature and reject the message.

Key rotation

The reason the YYYYMM format is used for the selector is that best practice calls for changing the DKIM signing keys every so often (monthly is recommended, and no longer than every 6 months). To do that without disrupting messages in transit, you generate the new keys using a new selector. The process is:

  1. Generate new keys as in step 8 of “Configuring OpenDKIM”. Do this in a scratch directory, not directly in /etc/opendkim/keys. Use the current year and month for the YYYYMM selector value, so it’s different from the selector currently in use.
  2. Use the newly-generated .txt files to add the new keys to DNS as in the DKIM “Setting Up DNS” section, using the new YYYYMM selector in the host names. Don’t remove or alter the existing DKIM TXT records.
  3. Stop Postfix and OpenDKIM by doing a sudo service postfix opendkim stop so that they won’t be processing mail while you’re changing out keys.
  4. Copy the newly-generated .private files into place and make sure their ownership and permissions are correct by running these commands from the directory in which you generated the key files:
    1
    2
    3
    cp *.private /etc/opendkim/keys/
    chown opendkim:opendkim /etc/opendkim/keys/*
    chmod go-rw /etc/opendkim/keys/*
    

    Use the opendkim-testkey command as described above to ensure that your new record is propogated before you continue.

  5. Edit /etc/opendkim/key.table and change the old YYYYMM values to the new selector, reflecting the current year and month. Save the file.
  6. Restart OpenDKIM and Postfix by:
    1
    2
    sudo service opendkim restart
    sudo service  postfix restart

    Make sure they both start without any errors.

  7. After a couple of weeks, all email in transit should either have been delivered or bounced and the old DKIM key information in DNS won’t be needed anymore. Delete the old YYYYMM._domainkeyTXT records in each of your domains, leaving just the newest ones (most recent year and month). Don’t worry if you forget and leave the old keys around longer than planned. There’s no security issue. Removing the obsolete records is more a matter of keeping things neat and tidy than anything else.

More Information

You may wish to consult the following resources for additional information on this topic. While these are provided in the hope that they will be useful, please note that we cannot vouch for the accuracy or timeliness of externally hosted materials.

参考资料

1、Configure SPF and DKIM in Postfix on Debian 8

2、Postfix on a null client

1 /etc/postfix/main.cf:
2     myhostname = hostname.example.com
3     myorigin = $mydomain
4     #relayhost = $mydomain
5     inet_interfaces = loopback-only
6     mydestination =