追追追:SSL中繼憑證迷行記
現在SSL憑證改成最長時效只有一年已經好幾年了。因此,一開始的故事很簡單,SSL憑證更新之後公司內網所有對此服務(我們暫定為:https://api.kkbruce.net/
,雖有點圖文不符,反正就是個含正式SSL憑證的端點)的請求一律失敗(如上圖)!真的是嚇死寶寶了。整個追蹤的時間很長,我盡量整理重點說明就好,但這一路下來,對於整個SSL憑證又多瞭解了一些。
開始:無法取得.p7c
重覆確認後確定,如果是在網路連線正常的環境下,這支請求是完全正常能通的。只有公司內網對它進行請求會出事問題。
由前圖可見,內網再向外網端點進行一個 HTTPS 的 /Token
請求,這個請求會 Faulted
。而在 /Token
的子請求可以發現,它去找 crt.sectigo.com
要拿一個 /SectigoPublicServerAuthenticationRootR46.p7c
憑證檔案回來,但請求被 Canceled
。
這裡有個好玩的地方,請求根本還沒進入到
/Token
裡,因此 Application Insights 記錄不到端點的其他日誌。
還好這裡老哥我有經驗,這單純就是 Firewall 沒開,檢查一下之前的記錄,之前的憑證是對 crt.enterprise.sectigo.com
來進行憑證驗證,今年新的憑證,它提供驗證檔案的端點換了,因此,請網路組同事開通一下 Firewall 就行了。
- sectigo.com (x)
- crt.sectigo.com (x)
- ocsp.sectigo.com (x)
- crt.enterprise.sectigo.com (v)
一開始只從 Application Insights 看到缺 crt.sectigo.com
,另外兩個(x)主要是透過 curl -v
與 openssl s_client -connect fqdn:443
兩個指令一段一段去拆解出來的。
Windows Server 的請求例外
Firewall 開通之後,從 Application Insights 可以清楚看到,對於 crt.sectigo.com
的請求已經正確無誤,因此請求有正確接觸到 /Token
端點。但在接觸到 /Token
點端的瞬間,服務立即就爆一個 AuthenticationException
出來。
訊息如下:Message:The SSL connection could not be established, see inner exception. The remote certificate is invalid because of errors in the certificate chain: PartialChain
這訊息讓我研究好久才搞通。我們不斷比對「通」與「不通」的請求與憑證。我們先看「通」的:
正常能通的請求,可以看到它的憑證結構上面有二層,一個是 Root(根憑證,圖中的 R46),另一個是 Intermediate(中繼憑證,圖中的 R36)。
那不通怎麼看?想想,它在找不到 Root / Intermediate 憑證時做了什麼事?它跑去下載 crt.sectigo.com/SectigoPublicServerAuthenticationRootR46.p7c
,這種憑證都是公開存取的,因此我們也能下載回來。
這裡個小技巧,下載回來後把
.p7c
改成.p7b
,這樣就能用 Windows 直接開啟。
很明顯的看到,它裡面只含了R46,沒有任何 R36 的影子。
也就是說,我們的應用程式在碰到外部 HTTPS 請求,並且在我們的 Windows/Linux 憑證管理區不存在的話,應用程式就會往外去求援,但他拿到的 .p7c 裡只有 R46 沒有 R36 的憑證,不足以驗證現在這張 SSL 憑證的合法性,因此我們的應用程式開出了火花,給了我們一個 AuthenticationException
例外,並且訊息寫著 PartialChain
的錯。從這方向來解釋這個 PartialChain
,我覺得就合情合理了。但這裡我有個疑問,少的那張 R36 就不能再發另一個請求去取得嗎,只能給我 PartialChain
的錯誤嗎?目前看起來答案是 Yes,但為什麼不能再發一個請求來取得 R36 憑證呢,我目前沒有找到答案。
知道問題點之後,那麼接下來怎麼解決?
解決辦法不難,就是在 Windows Server 去匯入缺少的那兩張Sectigo Root / Intermediate。由於這是個 Azure PaaS 服務,我們無法控制誰且在那台主機來呼叫,因此最後討論是請網路組同事去套 Policy 的方式,在全公司 Windows Server 去新增(匯入)這兩張 Sectigo Root / Intermediate 憑證。匯入後因為在能 Windows 憑證管理區找到需要的 Sectigo Root / Intermediate 憑證來驗證 SSL 憑證合法性,PartialChain
的應用程式錯誤就自然的消失了。
Linux Server 的請求例外
有了 Windows Server 的處理經驗,那麼 Linux Server 處理起來就會快很多。但 Linux Server 除了手動匯入之外,還有個自動化方案的機制。
Ubuntu Server 手動匯入為例,把準備好的新 CA 憑證檔複制到指令位置,然後更新一下 CA 憑證:
sudo cp local-ca.crt /usr/local/share/ca-certificates
sudo update-ca-certificates
透過 apt 更新:
sudo apt update
sudo apt install --reinstall ca-certificates
sudo update-ca-certificates
照理說,Ubuntu Server 預設在安裝時都有裝 ca-certificates
,而且平時如果你有在做 sudo apt update && sudo apt upgrade
更新的習慣,應該會發現,在 Linux Server 利用 curl
測試上不會不通。
簡單來說,如果是無法連網的環境,那麼就是手動匯入 CA 憑證。能連網的環境,透過 apt upgrade
應該就是解決問題,或是像我直接 reinstall
並重新套用,確保吃到最新的 CA 憑證清單資料。
Kubernetes Pod 的請求例外
標題更精準的說是「Container 的請求例外」。處理完 Host 本身的 HTTPS 請求問題之後,發現,在 Kubernetes Pod 內請發出 HTTPS 請求還是會發生一樣的問題。
kubectl run curl --rm --image=curlimages/curl -it -- sh
kubectl run busybox --rm --image=radial/busyboxplus:curl -it -- sh
這裡我找了兩組含 curl
的 image 來進行測試,結果發現一個非常好玩的結果。
curlimages/curl
:能正常完成請求。radial/busyboxplus:curl
:出現 HTTPS 錯誤,無法完成請求。
也就是說,我們還必須處理容器(image)內的 CA 憑證問題。這問題有許多種處理方案。
- C# 應用程式排除 HTTPS 驗證
- Dockerfile 加入更新 CA 憑證步驟
- Yaml 加入更新 CA 憑證步驟
- Kubenetes Volume(Yaml)
我個人認為,方案2從一開始的源頭就處理好是最佳解。
C# 應用程式排除 HTTPS 驗證
這在自簽憑證或公司內部憑證最常發生,簡單來就是透過 ServerCertificateCustomValidationCallback
去 bypass HTTPS 請求時的憑證驗證。強烈建議,不要抄網路上直接回傳 true
去 bypass 所有 HTTPS 請求的驗證,簡單寫點判斷式,只為必要的 domain 去 bypass,都比把所有驗證關閉來得好。
Dockerfile 加入更新 CA 憑證步驟
打不通,代表我們目前採用的 based image 的 CA 憑證清單不夠新,因此,比較好的方式是從源頭解決。在 Dockerfile 裡建置 image 的步驟加入以下指令碼,確保執行的容器內都含有最新的 CA 憑證清單。
sudo apt update
sudo apt install --reinstall ca-certificates
sudo update-ca-certificates
Yaml 加入更新 CA 憑證步驟
差不多同上 Dockerfile 的處理,只是更新 CA 憑證的動作改放在容器跑起來之前執行,利用如 Init Containers 之類合適的時機點先去完成 CA 憑證更新的動作。
Kubenetes Volume(Yaml)
有點類似手動的動作:
sudo cp local-ca.crt /usr/local/share/ca-certificates
sudo update-ca-certificates
這是利用 Volume 的機制,只是把 cp
改為用 Volume 去掛載新的 CA 憑證到容器裡指定的位置,讓容器能取得所需要的 CA 憑證。這裡有類似自動化的教學,有興趣可以參考一下。
小結
這是一張新的 SSL 憑證引發一連串的效應,一路追下來雖然費時(中間還有許多比對、驗證的過程被我省略了),但一關關的打通也是蠻有趣的經驗,並且網路上解決方案都解決不了問題時,在夜晚慢慢釐清思緒,一步一步的拆出來,會讓人不想睡覺(哈)。例如,一開始的 PartialChain
錯誤訊息,順著它從程式面去找根本是錯誤的方向。前後慢慢比對,為什麼前一張舊 SSL 憑證是正常的,換新 SSL 憑證就不正常,能連網的環境正常,內網環境又不正常,內網 Windows Server 不正常,但 Linux Server 又正常(我有在做 apt upgrade
),最後在容器又不正常,但也不是全部容器都不正常,不斷在 A 行 B 不行打轉,一開始都覺得鬼打牆,但慢慢釐清後才知道,它是對的,我是錯誤,只是我書讀太少。
但這裡也只解了我 95% 的疑問,最後那個神秘的 R36 為何不再發送請求再去取得的問題,我沒找到答案,留下小小遺憾。
沒有留言:
張貼留言
感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。