自動申請與更新 Let's Encrypt 萬用字元憑證 (內網適用)
| | | 0 | |
前天提到我為了要在內網網站啟用 HTTPS,學會用 mkcert 快速建立 CA 及網站憑證的簡便做法,mkcert 可以產生萬用字元憑證 (如:*.intra.net),所有主機共用一張憑證即可。但實際部署,才發現事情不像憨人想得那麼簡單...
為了要讓憑證能在包含電腦手機平板上都能順利顯示,我需要能建立或覆寫 DNS 記錄,讓 www. intra.net 對映到 192.168.1.1,這需要用網路設備動手腳,或另建 DNS 伺服器並讓客戶端改用私有 DNS 伺服器,而且我還學到新知識 - 把無線路由器 DHCP 的 DNS 指向私人 DNS 伺服器對手機是無效的,原因是現代手機為了安全預設不允許使用私人 DNS 伺服器,需要手動改設定:

第二件麻煩事是我得逐一在所有 PC、筆電、手機、平板上都下載、安裝並信任 mkcert 產生的這張 Root CA 憑證,我只設了自己的電腦跟平板就累了...
最後,我想到一個花錢省工的好方法,一年不到台幣 200 元,讓客戶部署零成本。
Let's Encrypt 憑證是所有客戶端直接承認,不需額外信任 CA 根憑證,但自動申請及定期更新,我之前都是跑 Nginx + certbot 容器,等 CA 發 HTTP 挑戰請求到網站時自動回應特定內容以通過驗證,這個前題是我必須是公開網站台 Let's Encrypt 可以連上,而且憑證為該網站專用,不能請一張 *.example.com 萬用字元憑證給所有網站用。其實,自動更新憑證協定也支援萬用字元憑證,但做法是要在 DNS 加一段指定 TXT 記錄證明我是網域的擁有者 (術語為 DNS-01 考驗,剛才說的做法叫 HTTP-01 考驗),如此我不需要有公開網站,也能申請到一張適用全網域所有網站,而且是全天下都認可的合法 TLS 憑證!
我打算去 Cloudflare 註冊一個網域名稱(最便宜的一年約 5 塊美金左右),Cloudflare 有提供 API 可自動新增修改 DNS 記錄,如此便可像 certbot 一樣實現自動申請、定期更新,豈不美哉?
(補充讀者分享:若不求好聽好記好輸入,Cloudflare 也有不到一美元的網域名稱)
經過一番研究,以下是我的做法。
我買了一個網域,先建立一支 API Token,權限部分限定只能修改特定網域的 DNS,並且只能更新(Edit) DNS 及讀取 Zone,依循資安原則,永遠只給最小權限,可降低損失風險:

建好 Token,會掉到 Account ID 及 API Token,Token 內容只會顯示一次,記得複製下來並妥善保管:

自動申請及定期更新我決定用 acme.sh 來做,連 Python 都不用裝,一支 Shell 程式搞定一切。
# 安裝
curl https://get.acme.sh | sh -s [email protected]
# 重新登入讓程式路徑設定生效
# 設定 Cloudflare 的 Account ID 及 API Token
export CF_Account_ID="Cloudflare Account ID"
export CF_Token="Cloundflare API Token"
# 成功執行後以上變數會加密寫入 ~/.acme.sh/account.conf,故只需設一次
acme.sh --issue --dns dns_cf -d example.com -d *.example.com
# 產生的憑證會寫到 ~/.acme.sh/domain-name_ecc 資料夾
# 若要安裝或更新到 nginx 可使用 --install-cert
acme.sh --install-cert -d example.com \
--key-file /etc/nginx/tls/example.com/cert.key \
--fullchain-file /etc/nginx/tls/example.com/cert.cer \
--reloadcmd "systemctl reload nginx" # 或 docker 指令
# 安裝時已自動加了 cron 排程,用 crontab -l 可確認
# 2 10 * * * "/home/jeffrey/.acme.sh"/acme.sh --cron --home "/home/jeffrey/.acme.sh" > /dev/null
# 強制更新
acme.sh --renew -d example.com --force
# 查詢憑證清單
acme.sh --list
照著以上操作,很快就得到一張同時適用 example.com 及 *.example.com 的有效 TLS 憑證:

等等,我這才發現 acme.sh 預設簽發的是 ZeroSSL 憑證,不是 Let's Encrypt。
追查發現,acme.sh 在 2020 年左右被 apilayer 公司收購,故將預設 CA 切換為 apilayer 自家的 ZeroSSL 也是合情合理滴 (Let's Encrypt 也有發公告)。而 acme.sh 對 Let's Encrypt 的支援完全沒改變,使用者也可透過以下步驟切回 Let's Encrypt:
# 更換預設 CA
acme.sh --set-default-ca --server letsencrypt
# 簽發憑證時指定 CA
acme.sh --issue --dns dns_cf --server letsencrypt -d example.com...
ZeroSSL 是商業公司,與 Let's Encrypt 相比,人工申請時需先註冊登入,但有介面可查詢管理憑證,另外的小優點是 ZeroSSL 對舊設備相容性更好。若透過 ACME 自動申請更新,則都是 90 天且免費,使用上感受不到任何差別。
但如果想對非營利的 Let's Encrypt 表達支持,可以像我一樣,將預設 CA 切成 Let's Encript 重簽一張~ 😄

新買的網域 DNS,可將 www. example.com 指向 192.168.1.1 這種內部 IP 完全沒問題,所有客戶端用任何 DNS 伺服器都能正確找到內網主機,不必自架 DNS 伺服器改設定,又少了件一堆麻煩。(然後,之前對外非固定 IP 自動更新 DDNS 的需求,在學會 Cloudfalre API 後,也可從免費 DDNS 換成更可靠的 Cloudflare DNS,我感覺被打開了一扇窗,世界又更開闊了,哈)
用一年一杯星巴客的錢,省下簽憑證、架 DNS、信任 CA 憑證這一串煩人頊事,超值得!!
Describes using Cloudflare DNS API with acme.sh to obtain and auto-renew wildcard TLS certificates via DNS-01. Compares mkcert vs public CAs, explains ZeroSSL vs Let’s Encrypt, and shows a low-cost HTTPS solution for internal networks.
Comments
Be the first to post a comment