服務器上的應用服務對外發送的一些 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 會出現證書過期的原因了。接下來看下如何解決:

  1. 修改服務器ca配置

  2. 更新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 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 :刪除過期證書後,記得要重啓主機上運行的服務!!!

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

6月19日,GNSEC 2020 全球新一代軟件工程線上峯會

復旦大學計算機科學技術學院副院長親自分享代碼大數據助力研發效能提升的精彩內容,敬請期待。

一個好機會,社區限時免費報名,僅限前 10 名,先到先得。

近期好文:

悄悄告訴你 MySQL MGR 牛在哪?

一文搞懂什麼是 vlan、三層交換機、網關、DNS、子網掩碼、MAC地址

“高效運維”公衆號誠邀廣大技術人員投稿,

投稿郵箱:[email protected],或添加聯繫人微信:greatops1118.

點擊閱讀原文,進入“GNSEC 線上峯會”官網

點個“在看”,一年不宕機

相關文章