Apache 從版本 2.4.8 開始,廢除了 SSLCertificateChainFile 這個 directive,直接由 SSLCertificateFile 處理整串憑證,降版的時候要小心。
症狀#
有的時候瀏覽器可以正常瀏覽,也就是說憑證是有效的,但是用 command-line tools 的時候網站的憑證卻會驗證不通過。
例如:
wget https://incomplete-chain.badssl.com/
原理#
憑證的簽發通常不會是直接由 root CA 直接簽發憑證給用戶(在這個案例裡就是 HTTPS 網站),而是 root CA 簽發憑證給 intermediate CA,再由 intermediate CA 簽發憑證給用戶。一般來說,在網頁瀏覽者的瀏覽器或作業系統裡,通常只存 root CA 的憑證資訊。然而,整套信任鏈的建立,仍然需要 intermediate CA 的憑證資訊,所以 HTTPS 伺服器在服務的時候,不只要提供自身的憑證,也要提供簽發自身憑證的 intermediate CA 憑證資訊。
用 OpenSSL 來看一個正常的範例:
echo -n | openssl s_client -connect badssl.com:443 -servername badssl.com -showcerts | less
裡面可以看到兩份憑證。
Apache 從版本 2.4.8 開始,直接把自身以及 intermediate CA 的憑證用單一檔案吃進來,過往則是分成兩個不同的檔案吃。Certbot 在替 Apache 站加 HTTPS 憑證及相關設定的時候,也會很聰明地判斷你所使用的 Apache 版本,生成符合該版本的設定。
如果是用 Certbot 重新跑一次設定當然不會有問題;直接把先前生成的設定檔複製一份來用的話,Apache 升版本應該也會因為新版不支援舊的 SSLCertificateChainFile directive,產生錯誤訊息而被發現。問題發生於新版降級到舊版的狀況,因為這個時候 Apache 不會有錯誤訊息,但是它卻只會從 SSLCertificateFile 所指向的 fullchain.pem 裡面拉出其中的自身憑證來用,並不處理 intermediate CA 的憑證資訊。
用 OpenSSL 來看一個這樣的範例:
echo -n | openssl s_client -connect incomplete-chain.badssl.com:443 -servername incomplete-chain.badssl.com -showcerts | less
這裡就只有一份憑證了。
在某些情況下瀏覽者並不會發現問題,因為用 Let’s Encrypt 的網站很多,瀏覽器有可能快取了從其他網站那裡拿到,剛剛好可以給你網站用的 intermediate CA 憑證資訊。但在沒有快取,或是快取被清掉的環境下,就會跳出憑證驗證不通過的警告。
修正#
降版時把設定檔裡的
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
改成分開的
SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem
SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem
反之(升版時)則逆向操作。
修正完畢後可以再用上述的 OpenSSL 檢查法確認伺服器是否正常給出所有所需憑證。