服务器上的应用服务对外发送的一些 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 https://sentry.xxx.com curl: (60) SSL certificate problem: certificate has expired More details here: https://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.
https://support.sectigo.com/articles/Knowledge/Sectigo-AddTrust-External-CA-Root-Expiring-May-30-2020
现在算是找到为什么请求 https 会出现证书过期的原因了。接下来看下如何解决:
主机(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 https://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,感兴趣的可以点击进去看看。
https://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:删除过期证书后,记得要重启主机上运行的服务!!!
领取专属 10元无门槛券
私享最新 技术干货