一个因 CA 根证书过期引起的故障,真相竟然是…

来自:高效运维 2020-06-17

服务器上的应用服务对外发送的一些 https 请求都失败了,真相竟然是……

问题

10点左右,同事反馈咨询线上的 Sentry 服务器 现在是否正常。之后去检查 Sentry 服务,运行正常,但是该应用服务对接的Sentry频道已经很久没有事件进来了。

感觉不太对劲,再去检查下 Sentry worker专用的容器,发现该Worker服务中中有些错误日志:

E, [2020-06-01T04:02:03.670850 #6] ERROR -- sentry: ** [Raven] Unable to record event with remote Sentry server (Raven::Error - SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (certificate has expired)):

/usr/local/bundle/gems/sentry-raven-2.7.3/lib/raven/transports/http.rb:34:in `rescue in send_event'

/usr/local/bundle/gems/sentry-raven-2.7.3/lib/raven/transports/http.rb:16:in `send_event'

/usr/local/bundle/gems/sentry-raven-2.7.3/lib/raven/client.rb:37:in `send_event'

/usr/local/bundle/gems/sentry-raven-2.7.3/lib/raven/instance.rb:81:in `send_event'

/app/src/worker.rb:26:in `perform'

/usr/local/bundle/gems/sidekiq-5.1.3/lib/sidekiq/processor.rb:187:in `execute_job'

/usr/local/bundle/gems/sidekiq-5.1.3/lib/sidekiq/processor.rb:169:in `block (2 levels) in process'

/usr/local/bundle/gems/sidekiq-5.1.3/lib/sidekiq/middleware/chain.rb:128:in `block in invoke'

/usr/local/bundle/gems/sentry-raven-2.7.3/lib/raven/integrations/sidekiq.rb:9:in `call'

/usr/local/bundle/gems/sidekiq-5.1.3/lib/sidekiq/middleware/chain.rb:130:in `block in invoke'

/usr/local/bundle/gems/sidekiq-5.1.3/lib/sidekiq/middleware/chain.rb:133:in `invoke'

E, [2020-06-01T04:02:03.671130 #6] ERROR -- sentry: ** [Raven] Failed to submit event: <no message value>

奇怪? sentry-worker 在连 sentry server 时请求域名的证书过期了?

分析

针对上面的错误信息,先去检查了相关调用的域名证书的有效期,发现都在有效期内。而且印象中都是年初刚更换的。所以排除了是域名证书问题。

然后根据错误日志,尝试在自己电脑上用下 curl 命令,巧合的很,也遇到了类似的错误。

$ curl //sentry.xxx.com

curl: (60) SSL certificate problem: certificate has expired

More details here: //curl.haxx.se/docs/sslcerts.html


curl failed to verify the legitimacy of the server and therefore could not

establish a secure connection to it. To learn more about this situation and

how to fix it, please visit the web page mentioned above.

我又去找了其它一台 Centos 主机,发现 curl 返回的结果是正常的,从 web 端和centos 客户端 curl 都成功的看,像是我本机电脑的 curl 和 sentry-worker 主机出了问题。

之后用到网上找到使用 openssl 命令排查ssl错误的方法:

$ openssl s_client -showcerts -servername sentry.xxx.com -connect sentry.xxx.com:443

CONNECTED(00000003)

depth=3 C = SE, O = AddTrust AB, OU = AddTrust External TTP Network, CN = AddTrust External CA Root

verify error:num=10:certificate has expired

notAfter=May 30 10:48:38 2020 GMT

---

Certificate chain

0 s:/OU=Domain Control Validated/OU=GoGetSSL Wildcard SSL/CN=*.xxx.com

i:/C=LV/L=Riga/O=GoGetSSL/CN=GoGetSSL RSA DV CA

-----BEGIN CERTIFICATE-----

#...省略

从上面执行命令返回的内容来看,这里的 CA 证书 AddTrust External CA Root 在 May 30 10:48:38 2020 GMT 这个时间过期了。

上网查了下相关的资料,发现他们官方发过一篇通告: Sectigo-AddTrust-External-CA-Root-Expiring-May-30-2020.

//support.sectigo.com/articles/Knowledge/Sectigo-AddTrust-External-CA-Root-Expiring-May-30-2020

解决方案

现在算是找到为什么请求 https 会出现证书过期的原因了。接下来看下如何解决:

    修改服务器ca配置

    更新ca库信息

主机(Ubuntu)

修改服务器 CA 配置

修改服务器 ca 证书配置文件: /etc/ca-certificates.conf

sed -i "/AddTrust_External_Root.crt/d" /etc/ca-certificates.conf

更新 CA 库

使用 update-ca-certificates 该命令用以更新目录 /etc/ssl/certs 来保存SSL证书,并生成 ca-certificates.crt:

$ sudo update-ca-certificates --fresh

Clearing symlinks in /etc/ssl/certs...

done.

Updating certificates in /etc/ssl/certs...

147 added, 0 removed; done.

Running hooks in /etc/ca-certificates/update.d...

done.

重启主机上的应用程序。

容器(Docker-Alpine OS)

容器同主机上的修改差不太多。修改ca配置文件,之后执行更新命令。以下以alpine系统为例:

修改配置文件:

sed -i "/AddTrust_External_Root.crt/d" /etc/ca-certificates.conf

更新 ca 证书链:

update-ca-certificates -f -v

当然上面的两条命令最好是放在 Dockerfile 中,你知道,在容器里做的任何修改都是不可靠的(除非挂载了共享或卷)。

Dockerfile 示例,在 CMD 之前添加一行:

省略...

RUN sed -i "/AddTrust_External_Root.crt/d" /etc/ca-certificates.conf \

&& update-ca-certificates -f -v

MacOS

我自己电脑上的curl也是同样的问题,目前没找到好的自动修改的方式。不过找到了系统上的 ca 文件路径。

先备份下文件:

sudo cp /etc/ssl/cert.pem ~/etc-ssl-cert.pem-20200601

之后,运行以下命令可以禁用掉过期的 CA 证书:

sudo sed -i "/^### AddTrust/,/^-.*END/ s/^/#/g" /etc/ssl/cert.pem

上面是注释掉,当然你也可以直接编辑文件删除这些行。

验证

修改更新完 ca 配置后,再次执行curl 命令去访问之前的网站:

$ curl //sentry.xxx.com

这次访问正常了。

其他问题

当时出现问题时,还有另外一个现象,就是用 curl 访问其他网站(如,bing.com、qq.com)都是正常的。怀疑是不是目标域名使用的证书链不一样, 导致了只有我们业务域名出现了问题呢?

验证下猜想

使用 openssl 检查下我们业务域名证书的链:

# openssl s_client -connect sentry.xxx.com:443

CONNECTED(00000003)

depth=2 C = US, ST = New Jersey, L = Jersey City, O = The USERTRUST Network, CN = USERTrust RSA Certification Authority

verify return:1

depth=1 C = LV, L = Riga, O = GoGetSSL, CN = GoGetSSL RSA DV CA

verify return:1

depth=0 OU = Domain Control Validated, OU = GoGetSSL Wildcard SSL, CN = *.xxx.com

verify return:1

---

Certificate chain

0 s:/OU=Domain Control Validated/OU=GoGetSSL Wildcard SSL/CN=*.xxx.com

i:/C=LV/L=Riga/O=GoGetSSL/CN=GoGetSSL RSA DV CA

1 s:/C=LV/L=Riga/O=GoGetSSL/CN=GoGetSSL RSA DV CA

i:/C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority

2 s:/C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority

i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root

---

Server certificate

省略...

在检测下上面说的其他网站 bing.com:

$ openssl s_client -connect cn.bing.com:443

CONNECTED(00000003)

depth=2 C = IE, O = Baltimore, OU = CyberTrust, CN = Baltimore CyberTrust Root

verify return:1

depth=1 C = US, ST = Washington, L = Redmond, O = Microsoft Corporation, OU = Microsoft IT, CN = Microsoft IT TLS CA 2

verify return:1

depth=0 CN = www.bing.com

verify return:1

---

Certificate chain

0 s:/CN=www.bing.com

i:/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/OU=Microsoft IT/CN=Microsoft IT TLS CA 2

1 s:/C=US/ST=Washington/L=Redmond/O=Microsoft Corporation/OU=Microsoft IT/CN=Microsoft IT TLS CA 2

i:/C=IE/O=Baltimore/OU=CyberTrust/CN=Baltimore CyberTrust Root

---

省略...

我又测试了其他几个大的站点,发现都正常。为什么唯独我们的SSL证书链中的其中一个 CA(Sectigo AddTrust)过期了呢?

去查找下相关的文章,关键词 sectigo gogetssl 2020 may 30.

发现第三条记录中有gogetssl发布的新闻,标题是:Sectigo AddTrust External CA Root Expired May 30, 2020,感兴趣的可以点击进去看看。

//www.gogetssl.com/news/23.html 

新闻大致的意思:

由于Sectigo AddTrust外部CA根证书过期,影响了一些旧的设备或者一些老服务器,因为上面的根证书链中还存在该过期的 CA 证书。对于客户端(浏览器/SDK)来说,他们是不受该 CA 过期的问题影响。所以最大影响是在server端. 可以通过下载最新的 AddTrust RSA 证书替换过期的。

总结

目前该问题的影响面广不广,这个还暂时未知,不过根据我遇到的情况来看,影响大多在服务器端的外部服务之间的调用。对web用户端来说,因为浏览器内证书链是更新的,不涉及该问题。但对于服务端来说,对于一些对外调用的 https 请求,如果对方域名证书链中涉及到该过期CA的话,可能会访问失败。

Tips1 :如果你的应用程序的部署方式是直接运行在主机上的话,可以使用配置管理工具(ansible/saltstack),统一修改。如果是容器话部署的情况,可能涉及的稍微多一些,需要修改项目的 Dockerfile,之后滚动更新该服务(当然如果你的应用不涉及到对外访问 https/ssl 调用,理论上可以延后更改!)。

Tips2 :删除过期证书后,记得要重启主机上运行的服务!!!

来源://aliasmee.github.io/post/resolve-certificate-verify-failed-with-2020-may-30/

6月19日,GNSEC 2020 全球新一代软件工程线上峰会

复旦大学计算机科学技术学院副院长亲自分享代码大数据助力研发效能提升的精彩内容,敬请期待。

一个好机会,社区限时免费报名,仅限前 10 名,先到先得。

近期好文:

悄悄告诉你 MySQL MGR 牛在哪?

一文搞懂什么是 vlan、三层交换机、网关、DNS、子网掩码、MAC地址

“高效运维”公众号诚邀广大技术人员投稿,

投稿邮箱:jiachen@greatops.net,或添加联系人微信:greatops1118.

点击阅读原文,进入“GNSEC 线上峰会”官网

点个“在看”,一年不宕机