一個(gè)因 CA 根證書過期引起的故障,真相竟然是…
服務(wù)器上的應(yīng)用服務(wù)對(duì)外發(fā)送的一些 https 請(qǐng)求都失敗了,真相竟然是……
問題
10點(diǎn)左右,同事反饋?zhàn)稍兙€上的Sentry 服務(wù)器現(xiàn)在是否正常。之后去檢查 Sentry 服務(wù),運(yùn)行正常,但是該應(yīng)用服務(wù)對(duì)接的Sentry頻道已經(jīng)很久沒有事件進(jìn)來了。
感覺不太對(duì)勁,再去檢查下 Sentry worker專用的容器,發(fā)現(xiàn)該Worker服務(wù)中中有些錯(cuò)誤日志:
- 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 時(shí)請(qǐng)求域名的證書過期了?
分析
針對(duì)上面的錯(cuò)誤信息,先去檢查了相關(guān)調(diào)用的域名證書的有效期,發(fā)現(xiàn)都在有效期內(nèi)。而且印象中都是年初剛更換的。所以排除了是域名證書問題。
然后根據(jù)錯(cuò)誤日志,嘗試在自己電腦上用下curl 命令,巧合的很,也遇到了類似的錯(cuò)誤。
- $ 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.
我又去找了其它一臺(tái) Centos 主機(jī),發(fā)現(xiàn) curl 返回的結(jié)果是正常的,從 web 端和centos 客戶端 curl 都成功的看,像是我本機(jī)電腦的 curl 和sentry-worker主機(jī)出了問題。
之后用到網(wǎng)上找到使用openssl命令排查ssl錯(cuò)誤的方法:
- $ 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-----
- #...省略
從上面執(zhí)行命令返回的內(nèi)容來看,這里的 CA 證書 AddTrust External CA Root 在 May 30 10:48:38 2020 GMT 這個(gè)時(shí)間過期了。
上網(wǎng)查了下相關(guān)的資料,發(fā)現(xiàn)他們官方發(fā)過一篇通告:Sectigo-AddTrust-External-CA-Root-Expiring-May-30-2020.
https://support.sectigo.com/articles/Knowledge/Sectigo-AddTrust-External-CA-Root-Expiring-May-30-2020
解決方案
現(xiàn)在算是找到為什么請(qǐng)求 https 會(huì)出現(xiàn)證書過期的原因了。接下來看下如何解決:
- 修改服務(wù)器ca配置
- 更新ca庫信息
主機(jī)(Ubuntu)
修改服務(wù)器 CA 配置
修改服務(wù)器 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.
重啟主機(jī)上的應(yīng)用程序。
容器(Docker-Alpine OS)
容器同主機(jī)上的修改差不太多。修改ca配置文件,之后執(zhí)行更新命令。以下以alpine系統(tǒng)為例:
修改配置文件:
- sed -i "/AddTrust_External_Root.crt/d" /etc/ca-certificates.conf
更新 ca 證書鏈:
- update-ca-certificates -f -v
當(dāng)然上面的兩條命令最好是放在 Dockerfile 中,你知道,在容器里做的任何修改都是不可靠的(除非掛載了共享或卷)。
Dockerfile 示例,在 CMD 之前添加一行:
省略...
- RUN sed -i "/AddTrust_External_Root.crt/d" /etc/ca-certificates.conf \
- && update-ca-certificates -f -v
- MacOS
我自己電腦上的curl也是同樣的問題,目前沒找到好的自動(dòng)修改的方式。不過找到了系統(tǒng)上的 ca 文件路徑。
先備份下文件:
- sudo cp /etc/ssl/cert.pem ~/etc-ssl-cert.pem-20200601
之后,運(yùn)行以下命令可以禁用掉過期的 CA 證書:
- sudo sed -i "/^### AddTrust/,/^-.*END/ s/^/#/g" /etc/ssl/cert.pem
上面是注釋掉,當(dāng)然你也可以直接編輯文件刪除這些行。
驗(yàn)證
修改更新完 ca 配置后,再次執(zhí)行curl 命令去訪問之前的網(wǎng)站:
- $ curl https://sentry.xxx.com
這次訪問正常了。
其他問題
當(dāng)時(shí)出現(xiàn)問題時(shí),還有另外一個(gè)現(xiàn)象,就是用 curl 訪問其他網(wǎng)站(如,bing.com、qq.com)都是正常的。懷疑是不是目標(biāo)域名使用的證書鏈不一樣, 導(dǎo)致了只有我們業(yè)務(wù)域名出現(xiàn)了問題呢?
驗(yàn)證下猜想
使用 openssl 檢查下我們業(yè)務(wù)域名證書的鏈:
- # 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
- 省略...
在檢測下上面說的其他網(wǎng)站 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
- ---
- 省略...
我又測試了其他幾個(gè)大的站點(diǎn),發(fā)現(xiàn)都正常。為什么唯獨(dú)我們的SSL證書鏈中的其中一個(gè) CA(Sectigo AddTrust)過期了呢?
去查找下相關(guān)的文章,關(guān)鍵詞 sectigo gogetssl 2020 may 30.
發(fā)現(xiàn)第三條記錄中有g(shù)ogetssl發(fā)布的新聞,標(biāo)題是:Sectigo AddTrust External CA Root Expired May 30, 2020,感興趣的可以點(diǎn)擊進(jìn)去看看。
https://www.gogetssl.com/news/23.html
新聞大致的意思:
由于Sectigo AddTrust外部CA根證書過期,影響了一些舊的設(shè)備或者一些老服務(wù)器,因?yàn)樯厦娴母C書鏈中還存在該過期的 CA 證書。對(duì)于客戶端(瀏覽器/SDK)來說,他們是不受該 CA 過期的問題影響。所以最大影響是在server端. 可以通過下載最新的 AddTrust RSA 證書替換過期的。
總結(jié)
目前該問題的影響面廣不廣,這個(gè)還暫時(shí)未知,不過根據(jù)我遇到的情況來看,影響大多在服務(wù)器端的外部服務(wù)之間的調(diào)用。對(duì)web用戶端來說,因?yàn)闉g覽器內(nèi)證書鏈?zhǔn)歉碌?,不涉及該問題。但對(duì)于服務(wù)端來說,對(duì)于一些對(duì)外調(diào)用的 https 請(qǐng)求,如果對(duì)方域名證書鏈中涉及到該過期CA的話,可能會(huì)訪問失敗。
Tips1:如果你的應(yīng)用程序的部署方式是直接運(yùn)行在主機(jī)上的話,可以使用配置管理工具(ansible/saltstack),統(tǒng)一修改。如果是容器話部署的情況,可能涉及的稍微多一些,需要修改項(xiàng)目的 Dockerfile,之后滾動(dòng)更新該服務(wù)(當(dāng)然如果你的應(yīng)用不涉及到對(duì)外訪問 https/ssl 調(diào)用,理論上可以延后更改?。?/p>
Tips2:刪除過期證書后,記得要重啟主機(jī)上運(yùn)行的服務(wù)!?。?/p>