SSL證書(shū)申請(qǐng)到Nginx部署實(shí)戰(zhàn):從原理到自動(dòng)續(xù)期)
說(shuō)實(shí)話免費(fèi)SSL證書(shū)這事我已經(jīng)幫不少人處理過(guò)了。每年總有幾個(gè)朋友過(guò)來(lái)問(wèn)同一類問(wèn)題“為什么我申請(qǐng)的免費(fèi)證書(shū)瀏覽器不認(rèn)”“證書(shū)到期忘了續(xù)簽網(wǎng)站掛了怎么辦”“照教程配置了Nginx怎么還是SSL連接錯(cuò)誤”。這些問(wèn)題單個(gè)看不復(fù)雜但它們通常集中在同一個(gè)環(huán)節(jié)——申請(qǐng)和部署之間的銜接沒(méi)有理解透。我打算把從零申請(qǐng)免費(fèi)SSL證書(shū)到把它穩(wěn)穩(wěn)部署在Nginx上、并且長(zhǎng)期自動(dòng)續(xù)期的完整過(guò)程寫(xiě)下來(lái)順便把我踩過(guò)、也看朋友踩過(guò)的坑都列出來(lái)。路線很簡(jiǎn)單先搞清楚方案再走一遍實(shí)操最后集中排查問(wèn)題。這篇指南里會(huì)覆蓋免費(fèi)證書(shū)和收費(fèi)證書(shū)的真實(shí)差別、主流簽發(fā)渠道怎么選、申請(qǐng)之前的準(zhǔn)備工作、用acme.sh和Certbot簽發(fā)證書(shū)的具體命令、Nginx部署與安全加固以及一大批SSL常見(jiàn)報(bào)錯(cuò)的排查思路。無(wú)論你是剛買(mǎi)了域名打算給網(wǎng)站加把鎖的新手還是被“證書(shū)鏈不完整”“ssl connection required”這類報(bào)錯(cuò)折磨過(guò)的運(yùn)維老哥應(yīng)該都能從這里找到答案。所有操作都是我實(shí)際跑通過(guò)的命令照著做基本能落地。1. 方案選型免費(fèi)證書(shū)的幾種主流渠道1.1 免費(fèi)證書(shū)和收費(fèi)證書(shū)的真實(shí)差別很多朋友一上來(lái)就在免費(fèi)和收費(fèi)之間糾結(jié)。說(shuō)實(shí)話你需要做的第一件事是把“免費(fèi)證書(shū)不安全”這個(gè)念頭從腦子里去掉。SSL證書(shū)的作用是證明兩件事第一這個(gè)域名確實(shí)是你控制的第二客戶端和服務(wù)器之間傳輸?shù)臄?shù)據(jù)經(jīng)過(guò)了加密。DV域名驗(yàn)證免費(fèi)證書(shū)已經(jīng)覆蓋了這兩個(gè)核心需求。收費(fèi)證書(shū)額外提供的通常是更高級(jí)的信任背書(shū)——比如OV證書(shū)會(huì)核實(shí)企業(yè)主體信息EV證書(shū)會(huì)在瀏覽器地址欄直接顯示公司名稱。這些對(duì)電商、金融、品牌官網(wǎng)來(lái)說(shuō)是有意義的但如果你只是個(gè)人博客、企業(yè)內(nèi)部系統(tǒng)、中小業(yè)務(wù)官網(wǎng)免費(fèi)DV證書(shū)在加密能力上和收費(fèi)證書(shū)沒(méi)有任何差別。真正拉開(kāi)差距的是有效期和維護(hù)成本免費(fèi)證書(shū)普遍90天有效期收費(fèi)證書(shū)一般一年或更久這意味著免費(fèi)方案要求你做好自動(dòng)化續(xù)期。這里有個(gè)容易混淆的點(diǎn)。很多人搜“ssl證書(shū)免費(fèi)申請(qǐng)”時(shí)想解決的其實(shí)是兩個(gè)完全不同的需求一個(gè)是網(wǎng)站部署HTTPS的服務(wù)器證書(shū)也就是這篇文章討論的另一個(gè)是軟件代碼簽名證書(shū)。代碼簽名證書(shū)需要用私鑰對(duì)軟件進(jìn)行數(shù)字簽名讓W(xué)indows等系統(tǒng)識(shí)別你的軟件發(fā)布者身份。到目前為止正規(guī)且被操作系統(tǒng)信任的代碼簽名證書(shū)很少提供免費(fèi)長(zhǎng)期方案這和網(wǎng)站SSL是兩碼事別搞混了。如果你是從“完全免費(fèi)的windows代碼簽名證書(shū)”這類關(guān)鍵詞過(guò)來(lái)的先明確自己的需求屬于哪一類再往下看。1.2 主流免費(fèi)證書(shū)渠道怎么選接下來(lái)是方案選型。我把國(guó)內(nèi)常用的免費(fèi)證書(shū)渠道做了個(gè)對(duì)比你可以直接按自己的場(chǎng)景選渠道有效期簽發(fā)方式適合場(chǎng)景注意事項(xiàng)Lets Encrypt90天ACME協(xié)議自動(dòng)簽發(fā)個(gè)人站、中小業(yè)務(wù)、自動(dòng)化運(yùn)維要求域名可正常解析80端口或DNS可被驗(yàn)證ZeroSSL90天ACME協(xié)議、網(wǎng)頁(yè)后臺(tái)需要ECC證書(shū)或Web面板操作申請(qǐng)流程多一步郵箱驗(yàn)證API也支持阿里云/騰訊云免費(fèi)證書(shū)通常12個(gè)月控制臺(tái)申請(qǐng)后審核國(guó)內(nèi)服務(wù)器環(huán)境免費(fèi)額度有限續(xù)期往往需要重新申請(qǐng)Cloudflare源站證書(shū)最長(zhǎng)15年面板一鍵生成已用Cloudflare做代理的站點(diǎn)只能在CF和源站之間使用不能替代公網(wǎng)證書(shū)從我的個(gè)人傾向來(lái)說(shuō)能上ACME的盡量上ACME。原因很直接ACME協(xié)議把簽發(fā)、部署、續(xù)期整個(gè)流程完全自動(dòng)化了。你只需要在最開(kāi)始跑一遍命令后面補(bǔ)上定時(shí)任務(wù)證書(shū)過(guò)期就不再是你能感知到的事情。用阿里云或騰訊云控制臺(tái)申請(qǐng)雖然看起來(lái)簡(jiǎn)單但它有一個(gè)讓人很頭疼的體驗(yàn)——免費(fèi)證書(shū)的續(xù)期往往不是“續(xù)”而是“重新申請(qǐng)”需要在控制臺(tái)手動(dòng)一步步提交審核通過(guò)后再手動(dòng)下載、上傳到服務(wù)器。一次兩次還好長(zhǎng)年累月一定會(huì)漏。如果你用的是國(guó)內(nèi)廠商的云服務(wù)器我仍建議首選Lets Encrypt這類ACME方案原因就一條少一個(gè)人工環(huán)節(jié)就少一個(gè)出錯(cuò)的概率。至于阿里云和騰訊云的免費(fèi)證書(shū)適合不想接觸命令行、業(yè)務(wù)量又不大的場(chǎng)景或者臨時(shí)給某個(gè)子域應(yīng)急用。兩者不沖突也可以同時(shí)用。2. 原理與準(zhǔn)備工作先搞清楚證書(shū)是怎么“被信任”的2.1 TLS證書(shū)和信任鏈到底是怎么工作的第一次做HTTPS部署的人往往會(huì)被一堆名詞繞暈“證書(shū)”“密鑰”“CA”“公鑰”“私鑰”“crt文件”“key文件”。這里用個(gè)生活類比你把網(wǎng)站想象成一個(gè)保險(xiǎn)柜柜子上有一把可以復(fù)制給任何人的鎖——這是公鑰你手里留一把只能你自己用的鑰匙——這是私鑰。瀏覽器訪問(wèn)網(wǎng)站時(shí)先把鎖公鑰證書(shū)拿過(guò)來(lái)把數(shù)據(jù)鎖進(jìn)箱子再寄過(guò)去只有你手里的私人鑰匙能打開(kāi)。問(wèn)題來(lái)了瀏覽器憑什么相信這把鎖是你的而不是中間人偽造的這就需要“證書(shū)信任機(jī)制”。CA機(jī)構(gòu)用自己的根證書(shū)給網(wǎng)站證書(shū)“簽字”瀏覽器和操作系統(tǒng)內(nèi)置了一批受信任的根證書(shū)。當(dāng)瀏覽器看到一張證書(shū)上有CA的合法簽名就會(huì)沿著簽名的鏈條往上找一直追到內(nèi)置的根證書(shū)。鏈條完整就信任斷掉就報(bào)警?,F(xiàn)在你能理解幾個(gè)常見(jiàn)報(bào)錯(cuò)背后的含義了。報(bào)“unable to get local issuer certificate”意思是瀏覽器沒(méi)辦法沿著證書(shū)鏈找到受信任根證書(shū)多半是你只部署了網(wǎng)站證書(shū)沒(méi)有把CA的中間證書(shū)一起配上去。報(bào)“此CA根目錄證書(shū)不受信任”通常是因?yàn)槟阌玫氖亲院灻C書(shū)沒(méi)有把它安裝到客戶端的信任庫(kù)。報(bào)“no required ssl certificate was sent”則出現(xiàn)在雙向認(rèn)證場(chǎng)景服務(wù)器要求客戶端出示證書(shū)但客戶端沒(méi)有帶。2.2 申請(qǐng)之前需要準(zhǔn)備好的清單明確了原理之后先把資質(zhì)和工具準(zhǔn)備齊能省下大量返工時(shí)間。一份能正常申請(qǐng)受信任免費(fèi)證書(shū)的檢查清單如下第一域名。這個(gè)必須有且必須由你掌握解析權(quán)限。IP地址不能申請(qǐng)Lets Encrypt公網(wǎng)證書(shū)因?yàn)镃A驗(yàn)證的就是你對(duì)該域名的控制權(quán)。常見(jiàn)驗(yàn)證方式有DNS解析一個(gè)TXT記錄、在站點(diǎn)根目錄放一個(gè)驗(yàn)證文件、或者通過(guò)HTTP訪問(wèn)一個(gè)特定路徑三種方式本質(zhì)上都是“證明你控制這個(gè)域名”。第二服務(wù)器。本地虛擬機(jī)也可以只要它能訪問(wèn)互聯(lián)網(wǎng)并跑Web服務(wù)。國(guó)內(nèi)部署還涉及服務(wù)器備案問(wèn)題這個(gè)根據(jù)你服務(wù)器的運(yùn)營(yíng)商要求辦跟證書(shū)申請(qǐng)本身是兩條線不要混著談。第三端口。HTTP驗(yàn)證方式需要80端口或443端口可被外網(wǎng)訪問(wèn)DNS驗(yàn)證方式則不需要開(kāi)放端口但需要你能登錄域名服務(wù)商的控制臺(tái)操作解析記錄。第四Web服務(wù)。這里優(yōu)先推薦Nginx后續(xù)我會(huì)給出完整配置。Apache、Caddy、Tomcat也都可以原理一致只是配置指令不同。第五聯(lián)系方式。用ACME方式注冊(cè)時(shí)需要提供一個(gè)有效郵箱接收過(guò)期提醒用云廠商控制臺(tái)申請(qǐng)時(shí)用賬號(hào)登錄就行。這些準(zhǔn)備工作里大家最容易卡在域名解析上。如果解析還沒(méi)生效就去簽發(fā)CA訪問(wèn)不到你的域名驗(yàn)證直接失敗。建議先執(zhí)行ping yourdomain.com或者dig yourdomain.com確認(rèn)解析生效再進(jìn)入申請(qǐng)環(huán)節(jié)。3. 實(shí)操三分鐘申請(qǐng)第一個(gè)免費(fèi)證書(shū)3.1 用acme.sh簽發(fā)Lets Encrypt證書(shū)Lets Encrypt是目前用戶量最大的免費(fèi)CA幾乎所有自動(dòng)化方案都圍繞它展開(kāi)。我推薦使用acme.sh它用Shell腳本實(shí)現(xiàn)ACME協(xié)議安裝輕量、支持多種驗(yàn)證方式和DNS服務(wù)商API擴(kuò)展性最好。第一步安裝acme.sh。在服務(wù)器上執(zhí)行curl https://get.acme.sh | sh安裝腳本會(huì)把程序放到~/.acme.sh/目錄并自動(dòng)配置環(huán)境變量。裝完之后需要重新登錄一次終端或者執(zhí)行source ~/.bashrc讓命令行里能找到acme.sh命令。第二步注冊(cè)賬號(hào)。Lets Encrypt要求提供一個(gè)有效郵箱用于接收證書(shū)過(guò)期提醒和緊急通知acme.sh --register-account -m youremailexample.com如果你用的是國(guó)內(nèi)服務(wù)器在某些網(wǎng)絡(luò)環(huán)境下Lets Encrypt的API可能連接不穩(wěn)定這時(shí)可以用--server參數(shù)切換到ZeroSSL的ACME端點(diǎn)命令差別不大。第三步簽發(fā)證書(shū)。以最常見(jiàn)的webroot模式為例驗(yàn)證文件會(huì)被放到Nginx根目錄下CA通過(guò)訪問(wèn)特定地址來(lái)確認(rèn)域名控制權(quán)acme.sh --issue -d example.com -d www.example.com --webroot /var/www/html這里的-d參數(shù)可以重復(fù)使用用于申請(qǐng)多域名證書(shū)。簽出來(lái)的證書(shū)會(huì)自動(dòng)包含你指定的所有域名瀏覽器訪問(wèn)其中任意一個(gè)都會(huì)匹配不需要再分開(kāi)申請(qǐng)。執(zhí)行成功后證書(shū)和私鑰會(huì)存放在~/.acme.sh/example.com/目錄下。需要說(shuō)明的是這個(gè)目錄是acme.sh的工作目錄不建議直接把Nginx的配置指向這里正確的做法是把證書(shū)安裝到目標(biāo)目錄這一點(diǎn)我在部署部分會(huì)講。如果域名解析還沒(méi)就緒或者80端口暫時(shí)無(wú)法對(duì)外訪問(wèn)可以改用DNS驗(yàn)證模式acme.sh --issue -d example.com --dns執(zhí)行后它會(huì)給出一個(gè)TXT記錄值你把它添加到域名解析控制臺(tái)等一兩分鐘生效后重新運(yùn)行即可。這種方式不依賴Web服務(wù)器尤其適合申請(qǐng)泛域名證書(shū)通配符證書(shū)不過(guò)對(duì)DNS服務(wù)商的操作權(quán)限有一定要求。3.2 Certbot的另一種方案如果你不喜歡手動(dòng)折騰腳本Certbot提供了更“開(kāi)箱即用”的體驗(yàn)。在Debian/Ubuntu系統(tǒng)上安裝apt install certbot python3-certbot-nginx如果Nginx站點(diǎn)配置已經(jīng)寫(xiě)好了server_name直接執(zhí)行certbot --nginx -d example.com -d www.example.comCertbot會(huì)詢問(wèn)一些選項(xiàng)比如是否自動(dòng)把HTTP重定向到HTTPS按提示選擇就行。它甚至?xí)樖謳湍惆袾ginx配置里的SSL段全部改好這點(diǎn)比acme.sh更省心。但它的自動(dòng)化程度不如acme.sh靈活尤其是當(dāng)你要把證書(shū)安裝到特定目錄或者對(duì)接自己運(yùn)維體系的時(shí)候。個(gè)人建議是單機(jī)單站用Certbot非常舒服服務(wù)器數(shù)量多、域名變動(dòng)頻繁、將來(lái)可能要對(duì)接DNS服務(wù)商API做泛域名證書(shū)的直接上acme.sh一步到位。3.3 證書(shū)文件到底怎么對(duì)應(yīng)簽好證書(shū)之后你會(huì)看到幾個(gè)后綴不同的文件不少人的混亂就是從這兒開(kāi)始的。以acme.sh為例最常用的有這幾個(gè)example.com.key私鑰相當(dāng)于前面類比里的私人鑰匙必須嚴(yán)格保密絕不能泄露到公網(wǎng)。example.com.cer網(wǎng)站證書(shū)葉證書(shū)包含你的域名信息和公鑰。fullchain.cer完整證書(shū)鏈包含網(wǎng)站證書(shū)加中間證書(shū)。部署時(shí)一定要用這個(gè)文件而不是單獨(dú)的cer文件。很多人配完只有一個(gè)cer和一個(gè)key結(jié)果瀏覽器報(bào)警“證書(shū)鏈不完整”就是漏了中間證書(shū)。用cat 網(wǎng)站證書(shū) 中間證書(shū) 完整證書(shū)鏈的方式也能手工合成但既然簽發(fā)工具已經(jīng)生成了fullchain直接拿來(lái)用就行。我記得第一次自己簽發(fā)證書(shū)時(shí)不太清楚這些文件有什么區(qū)別把單張證書(shū)當(dāng)成完整鏈配了上去當(dāng)時(shí)想法很簡(jiǎn)單——“證書(shū)不就是那一張嗎”。后來(lái)在多個(gè)客戶端上測(cè)試才意識(shí)到信任鏈?zhǔn)菫g覽器向根證書(shū)追溯的關(guān)鍵少了一環(huán)就容易在部分設(shè)備上報(bào)錯(cuò)。而且這類問(wèn)題在桌面瀏覽器上不一定馬上暴露某些移動(dòng)端App或老版本系統(tǒng)里卻會(huì)翻車(chē)。4. 把證書(shū)部署到Nginx并順手做好安全加固4.1 Nginx里的最小可用配置證書(shū)簽發(fā)成功只是第一步能不能讓瀏覽器認(rèn)還得看服務(wù)端配得對(duì)不對(duì)。先把證書(shū)安裝到Nginx配置目錄mkdir -p /etc/nginx/ssl cp ~/.acme.sh/example.com/fullchain.cer /etc/nginx/ssl/ cp ~/.acme.sh/example.com/example.com.key /etc/nginx/ssl/然后編輯站點(diǎn)配置文件加上443端口的server塊server { listen 443 ssl http2; server_name example.com www.example.com; ssl_certificate /etc/nginx/ssl/fullchain.cer; ssl_certificate_key /etc/nginx/ssl/example.com.key; root /var/www/html; index index.html; }配置好后測(cè)試并重載Nginxnginx -t systemctl reload nginx這樣HTTPS已經(jīng)能正常訪問(wèn)了。但要注意如果之前有監(jiān)聽(tīng)80端口的server塊最好添加一個(gè)跳轉(zhuǎn)把HTTP流量統(tǒng)一導(dǎo)到HTTPS避免用戶習(xí)慣性輸入域名時(shí)看到明文頁(yè)面server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; }HTTP/2如果需要啟用得看Nginx版本。老版本Linux發(fā)行版自帶的Nginx可能沒(méi)有編譯http2模塊版本不夠的情況下建議先升級(jí)Nginx再啟用。4.2 順手做幾個(gè)安全加固項(xiàng)證書(shū)配上之后強(qiáng)烈建議把下面幾項(xiàng)也一起做了都不是復(fù)雜操作但對(duì)站點(diǎn)安全性提升非常明顯。第一限制TLS協(xié)議版本。SSLv3、TLSv1.0、TLSv1.1都已經(jīng)是過(guò)時(shí)協(xié)議存在公開(kāi)漏洞實(shí)踐中應(yīng)禁用。在server塊里加ssl_protocols TLSv1.2 TLSv1.3;第二合理配置加密套件。參考Mozilla推薦的現(xiàn)代配置可以在兼容性和安全性之間取得平衡ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off;第三開(kāi)啟HSTS。告訴瀏覽器接下來(lái)一段時(shí)間內(nèi)只能用HTTPS訪問(wèn)本站能有效防止會(huì)話劫持add_header Strict-Transport-Security max-age63072000; includeSubDomains; preload always;需要注意HSTS一旦開(kāi)啟半年內(nèi)如果臨時(shí)想用HTTP訪問(wèn)瀏覽器也會(huì)強(qiáng)制跳轉(zhuǎn)測(cè)試環(huán)境的站點(diǎn)別急著開(kāi)這個(gè)頭。第四開(kāi)啟OCSP Stapling。瀏覽器不再逐個(gè)去CA查詢證書(shū)吊銷(xiāo)狀態(tài)而是由你的Nginx代為查詢并附帶在握手過(guò)程中既提升訪問(wèn)速度又減少隱私泄露ssl_stapling on; ssl_stapling_verify on; resolver 8.8.8.8 1.1.1.1 valid300s;做完這四項(xiàng)免費(fèi)證書(shū)的安全性已經(jīng)達(dá)到常規(guī)生產(chǎn)環(huán)境的水準(zhǔn)了。4.3 需要雙向認(rèn)證時(shí)怎么處理有一些朋友在做內(nèi)部系統(tǒng)比如后端API對(duì)接或者Tomcat服務(wù)會(huì)搜到“ssl雙向認(rèn)證tomcat下如何配置”這類詞。雙向認(rèn)證的意思是在常規(guī)TLS握手之外服務(wù)器也要驗(yàn)證客戶端的證書(shū)。典型場(chǎng)景是企業(yè)內(nèi)部接口既要防止數(shù)據(jù)在傳輸中被竊聽(tīng)又要確保只有裝了指定客戶端證書(shū)的機(jī)器才能訪問(wèn)。雙向認(rèn)證和單向認(rèn)證的區(qū)別在于服務(wù)器在握手時(shí)會(huì)要求客戶端出示證書(shū)拿不到或者證書(shū)無(wú)法通過(guò)信任鏈校驗(yàn)直接拒絕連接。對(duì)應(yīng)到Nginx配置需要額外加兩條ssl_client_certificate /etc/nginx/ssl/ca.crt; ssl_verify_client on;前面提到的“no required ssl certificate was sent”就是典型的客戶端沒(méi)帶證書(shū)報(bào)錯(cuò)。這種場(chǎng)景通常會(huì)用自建CA簽發(fā)客戶端證書(shū)然后把CA證書(shū)配置到ssl_client_certificate中。注意一點(diǎn)自建CA簽發(fā)的證書(shū)不屬于公共信任體系它只服務(wù)于你的內(nèi)部系統(tǒng)和外面申請(qǐng)的公共受信任證書(shū)是兩套邏輯別混在一起配置。5. 常見(jiàn)問(wèn)題與排查免費(fèi)證書(shū)實(shí)戰(zhàn)中的那些坑5.1 瀏覽器報(bào)“證書(shū)鏈不完整”或“unable to get local issuer certificate”這是反饋率最高的報(bào)錯(cuò)之一。出現(xiàn)這個(gè)問(wèn)題的原因大多是只配置了葉證書(shū)cer而沒(méi)配置中間證書(shū)。解決方案很直接把ssl_certificate指向fullchain.cer。如果你是從云廠商控制臺(tái)下載的證書(shū)包一般里面會(huì)包含xxx.pem、xxx.key、xxx_ca.pem三個(gè)文件配置時(shí)要注意ssl_certificate /path/to/xxx.pem; ssl_certificate_key /path/to/xxx.key; ssl_trusted_certificate /path/to/xxx_ca.pem;ssl_trusted_certificate的作用是把中間證書(shū)喂給OCSP校驗(yàn)有些人會(huì)漏掉這一行。雖然瀏覽器訪問(wèn)時(shí)不一定立刻報(bào)錯(cuò)但在某些客戶端環(huán)境下就會(huì)暴露鏈不完整的問(wèn)題。排查時(shí)還可以用一條命令快速看服務(wù)器實(shí)際返回的證書(shū)鏈openssl s_client -connect example.com:443 -servername example.com如果輸出里只有一段證書(shū)而沒(méi)有中間證書(shū)那基本可以確定是鏈沒(méi)配全。5.2 各種“SSL握手失敗”和“連接錯(cuò)誤”常見(jiàn)報(bào)錯(cuò)包括“ssl handshake failure”“ssl recv :服務(wù)器不支持ssl”“SSL connection required”等。這類問(wèn)題的原因比證書(shū)鏈復(fù)雜需要逐個(gè)排查服務(wù)端沒(méi)開(kāi)443端口或防火墻攔截。云服務(wù)器的安全組也需要檢查只改系統(tǒng)防火墻有時(shí)會(huì)遺漏??蛻舳撕头?wù)端TLS版本不兼容。老系統(tǒng)上跑的程序只支持TLSv1.0/1.1而新版OpenSSL可能已經(jīng)默認(rèn)禁用了這些版本需要在兩端做匹配。域名和證書(shū)不匹配。證書(shū)只簽了example.com訪問(wèn)卻是www.example.com或者用IP訪問(wèn)都會(huì)觸發(fā)握手失敗。服務(wù)端沒(méi)有正確加載證書(shū)。Nginx配置完成后沒(méi)reload后臺(tái)還跑著舊配置也會(huì)造成類似的報(bào)錯(cuò)。遇到這類報(bào)錯(cuò)不要憑直覺(jué)打補(bǔ)丁。先用openssl s_client驗(yàn)證服務(wù)端基礎(chǔ)狀態(tài)再逐段確認(rèn)端口、證書(shū)路徑、配置語(yǔ)法按順序排查最快。5.3 自簽名證書(shū)和“此CA根目錄證書(shū)不受信任”很多內(nèi)網(wǎng)項(xiàng)目使用自簽名證書(shū)因?yàn)閮?nèi)部系統(tǒng)沒(méi)必要申請(qǐng)公共證書(shū)自己生成一張就行。但自簽名證書(shū)不屬于任何受信任CA瀏覽器默認(rèn)不信任會(huì)彈“此CA根目錄證書(shū)不受信任”之類的告警。處理方式有三種。第一種把自簽名CA證書(shū)導(dǎo)出安裝到各客戶端的“受信任的根證書(shū)頒發(fā)機(jī)構(gòu)”里適合內(nèi)網(wǎng)統(tǒng)一管控的場(chǎng)景。第二種在Nginx里直接配置自簽名證書(shū)客戶端訪問(wèn)時(shí)手動(dòng)忽略告警適合臨時(shí)測(cè)試環(huán)境。第三種搭建內(nèi)網(wǎng)私有CA用于需要大量簽發(fā)證書(shū)的團(tuán)隊(duì)這需要額外構(gòu)建一套PKI體系一般企業(yè)級(jí)應(yīng)用才會(huì)用到。順帶提一下代碼簽名證書(shū)也屬于“自簽名不被信任”的高發(fā)區(qū)。Windows系統(tǒng)不會(huì)信任未知發(fā)布者的軟件簽名正規(guī)解決路徑是購(gòu)買(mǎi)受信任CA簽發(fā)的代碼簽名證書(shū)。免費(fèi)且長(zhǎng)期有效的代碼簽名方案目前并沒(méi)有穩(wěn)定落地別被某些“免費(fèi)證書(shū)”宣傳誤導(dǎo)了。5.4 MySQL和Java客戶端的經(jīng)典報(bào)錯(cuò)開(kāi)發(fā)場(chǎng)景里的SSL報(bào)錯(cuò)很大比例集中在數(shù)據(jù)庫(kù)連接和Java程序上。MySQL這邊常見(jiàn)的是第一種[08001] ssl connection required, but not provided by server.意思是服務(wù)器要求SSL但客戶端沒(méi)有啟用SSL。處理方式是在連接參數(shù)中加上useSSLtrue并配置CA證書(shū)如果后端全在內(nèi)網(wǎng)且已有其他防護(hù)也可以把服務(wù)器的require_secure_transport參數(shù)關(guān)掉再重啟MySQL。第二種是證書(shū)不匹配或CA校驗(yàn)失敗需要在JDBC URL或客戶端配置里指定--ssl-ca參數(shù)。Java這邊比較經(jīng)典的還有PKIX path building failed或者unable to find valid certification path to requested target。Java運(yùn)行環(huán)境帶了一個(gè)信任庫(kù)cacerts里面默認(rèn)只放公共CA的根證書(shū)。如果你的服務(wù)端用的是自簽名證書(shū)Java客戶端不會(huì)認(rèn)。解決辦法是把對(duì)方CA證書(shū)導(dǎo)入信任庫(kù)keytool -import -alias example -file ca.crt -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit導(dǎo)入后重啟Java服務(wù)即可。在本地開(kāi)發(fā)調(diào)試時(shí)直接用-Djavax.net.ssl.trustStore...指定自定義信任庫(kù)會(huì)更靈活不會(huì)污染全局環(huán)境。5.5 抓包工具證書(shū)安裝后仍然顯示unknown移動(dòng)端開(kāi)發(fā)同學(xué)最容易踩的坑就是這個(gè)。Charles、Fiddler、mitmproxy這類抓包工具能解密HTTPS流量原理是在手機(jī)上安裝工具自身的根證書(shū)讓代理服務(wù)器以中間人的身份建立兩條TLS連接。你手機(jī)確實(shí)安裝了證書(shū)但抓包依然顯示“unknown”或“SSL連接錯(cuò)誤”。原因通常是Android 7.0以上版本默認(rèn)不再信任用戶安裝的CA證書(shū)只信任系統(tǒng)級(jí)CA。即便你把證書(shū)裝上了應(yīng)用也不認(rèn)。處理辦法有三個(gè)方向一是用adb把用戶證書(shū)移動(dòng)到系統(tǒng)證書(shū)目錄前提是設(shè)備已root或刷了可寫(xiě)系統(tǒng)的ROM二是修改App的調(diào)試配置在AndroidManifest里聲明只信任調(diào)試用的證書(shū)三是在測(cè)試環(huán)境里暫時(shí)關(guān)閉App的SSL Pinning校驗(yàn)——但這一步非常謹(jǐn)慎它只屬于測(cè)試手段不能進(jìn)入生產(chǎn)代碼。iOS端相對(duì)簡(jiǎn)單安裝證書(shū)后還要去“設(shè)置-通用-關(guān)于本機(jī)-證書(shū)信任設(shè)置”里手動(dòng)開(kāi)啟完全信任漏掉這一步同樣會(huì)失敗。5.6 免費(fèi)證書(shū)“續(xù)期”其實(shí)是偽命題如果你用的是云廠商控制臺(tái)申請(qǐng)的免費(fèi)證書(shū)會(huì)發(fā)現(xiàn)在證書(shū)到期前看到的操作不是“續(xù)期”而是“重新申請(qǐng)”。阿里云、騰訊云這類廠商的免費(fèi)證書(shū)通常都是讓你重新提交申請(qǐng)、重新審核、再重新下載整個(gè)流程無(wú)法一鍵自動(dòng)化。申請(qǐng)本身不復(fù)雜但半年或一年就要重復(fù)一輪非常容易忘。解決思路有兩個(gè)一是直接把證書(shū)簽發(fā)遷到ACME體系通過(guò)定時(shí)任務(wù)自動(dòng)續(xù)期從此不用關(guān)注證書(shū)有效期二是搭建證書(shū)過(guò)期監(jiān)控提前檢查證書(shū)剩余天數(shù)到期前發(fā)送告警。檢查命令很簡(jiǎn)單echo | openssl s_client -connect example.com:443 -servername example.com 2/dev/null | openssl x509 -noout -dates把這條命令加進(jìn)監(jiān)控系統(tǒng)或者crontab每天跑一次輸出里的notAfter就是過(guò)期時(shí)間。自動(dòng)續(xù)期加過(guò)期監(jiān)控雙保險(xiǎn)才能徹底擺脫“某天早上發(fā)現(xiàn)網(wǎng)站掛了”的命。6. 自動(dòng)續(xù)期免費(fèi)證書(shū)最不能省的一步6.1 給acme.sh裝上自動(dòng)續(xù)期既然免費(fèi)證書(shū)的有效期只有90天自動(dòng)續(xù)期就是必須養(yǎng)成的習(xí)慣。acme.sh在安裝時(shí)就會(huì)注冊(cè)一個(gè)每日定時(shí)任務(wù)檢查所有證書(shū)的剩余有效期當(dāng)剩余天數(shù)小于60天時(shí)自動(dòng)重新簽發(fā)。不過(guò)這還不夠自動(dòng)續(xù)期之后證書(shū)文件只是更新在~/.acme.sh目錄里你還需要一個(gè)“安裝”動(dòng)作把新證書(shū)復(fù)制到Nginx用的目錄并觸發(fā)reload。acme.sh 提供了內(nèi)置的--install-cert參數(shù)可以在證書(shū)更新時(shí)執(zhí)行自定義命令acme.sh --install-cert -d example.com \ --key-file /etc/nginx/ssl/example.com.key \ --fullchain-file /etc/nginx/ssl/fullchain.cer \ --reloadcmd systemctl reload nginx當(dāng)acme.sh自動(dòng)續(xù)期成功后它會(huì)自動(dòng)執(zhí)行--reloadcmd指定的命令把Nginx重載讓新證書(shū)立即生效。這一步配置一次之后整個(gè)周期全自動(dòng)基本不需要人工干預(yù)。6.2 定時(shí)任務(wù)與證書(shū)過(guò)期監(jiān)控順手驗(yàn)證一下定時(shí)任務(wù)是否在運(yùn)行crontab -l | grep acme你應(yīng)該能看到類似0 0 * * * ... acme.sh ...的一行。如果因?yàn)榄h(huán)境遷移或權(quán)限問(wèn)題導(dǎo)致定時(shí)任務(wù)丟失用acme.sh --install-cronjob重新安裝即可。最后再分享一個(gè)我在生產(chǎn)環(huán)境里學(xué)到的教訓(xùn)不要把自動(dòng)續(xù)期當(dāng)成萬(wàn)能保險(xiǎn)。定時(shí)任務(wù)本身有可能因?yàn)榉?wù)重啟、服務(wù)器遷移、DNS解析變化等原因中斷一旦中斷沒(méi)人發(fā)現(xiàn)證書(shū)就會(huì)在你毫無(wú)察覺(jué)的情況下過(guò)期。所以一定要有獨(dú)立的監(jiān)控手段哪怕只是每天跑一條命令檢查證書(shū)剩余天數(shù)也比完全盲跑要好。監(jiān)控告警可以接入你現(xiàn)有的告警渠道企業(yè)微信、釘釘或者郵件都行這樣能避免“某天早上發(fā)現(xiàn)網(wǎng)站掛了”這種驚魂事件。我個(gè)人在實(shí)際操作中的體會(huì)是免費(fèi)SSL證書(shū)如今已經(jīng)是非常成熟的基礎(chǔ)設(shè)施它并不比收費(fèi)方案差真正決定成敗的往往是使用習(xí)慣。我見(jiàn)過(guò)太多因?yàn)樽C書(shū)過(guò)期導(dǎo)致的線上事故每一個(gè)都是自動(dòng)化沒(méi)做好或者續(xù)期流程過(guò)度依賴人工。如果你讀完這篇文章只打算做一件事那就把自動(dòng)續(xù)期配置好然后定期檢查證書(shū)剩余天數(shù)。免費(fèi)的從來(lái)不是最貴的最貴的是過(guò)期之后才想起它。祝各位申請(qǐng)順利別再被SSL報(bào)錯(cuò)折騰。