Certbot 如何申請 DNS-01 萬用字元憑證
了解 Certbot 透過 DNS-01 challenge 申請萬用字元憑證,如何建立 _acme-challenge TXT record、安裝 DNS plugin,並讓續期自動完成。
為什麼萬用字元憑證需要 DNS-01
萬用字元憑證涵蓋網域的所有第一層子網域:*.example.com 可匹配 app.example.com、blog.example.com,以及其他任何深度為一個標籤的名稱。Let’s Encrypt 只會透過 DNS-01 challenge 核發萬用字元憑證,因此 Certbot 必須在 _acme-challenge.example.com 發布 TXT record,以證明你能控制該網域的 DNS。HTTP-01 challenge 不符合此需求,因為提供 token 檔案只能證明你能控制某個主機名稱,也就是 validation server 取得該檔案的名稱。萬用字元憑證涵蓋該網域下所有可能的名稱,而能代表整個名稱空間的唯一公開記錄就是 DNS。
這項要求會決定本頁的其他設定。若要通過 DNS-01,你必須能在該網域的 zone 中建立 TXT records,可以手動建立,也可以透過 DNS provider 的 API(application programming interface)建立。手動方式只能成功一次,續期時會失敗,具體原因如下所示。透過 Certbot DNS plugin 使用 API,則可在無人介入的情況下自動續期;最後應採用這種設定。
這是 Certbot 指南中的萬用字元章節。一般的單一主機名稱憑證、web server 設定及 port 80 規則,請參閱 在 Ubuntu 24.04 上使用 nginx 設定 Certbot 和 在 Ubuntu 24.04 上使用 Apache 設定 Certbot。
_acme-challenge TXT 記錄的運作方式
Certbot 要求 *.example.com 時,Let's Encrypt 會回傳隨機權杖。Certbot 將該權杖與您的 ACME(自動憑證管理環境)帳戶金鑰組合,以 SHA-256 計算雜湊,並產生一段簡短的文字值。該值必須以 TXT 記錄形式出現在 _acme-challenge.example.com。接著,Let's Encrypt 會從自己的基礎架構查詢您網域的權威名稱伺服器。如果讀取到的記錄符合預期值,就表示您已證明自己控制該 zone;而對該 zone 的控制權,也會被視為對其下所有名稱的控制權。
以下兩個細節最常造成驗證失敗:
- 在同一張憑證中要求
example.com和*.example.com,代表需要進行兩個獨立的挑戰,而兩筆 TXT 記錄都位於同一個名稱_acme-challenge.example.com。兩筆記錄必須同時存在。加入第二筆記錄是正確做法;以第二筆取代第一筆,會導致第一個挑戰失敗。 - 驗證會讀取您的權威伺服器,但 DNS provider 的控制台可能需要 1 分鐘以上,才能將新記錄推送至這些伺服器。讓驗證開始前,先從外部檢查:
dig +short TXT _acme-challenge.example.com @1.1.1.1如果輸出 Certbot 要求的值,驗證就可能成功。如果沒有輸出任何內容,請稍候再重新執行。
實際執行一次:手動模式
手動模式要求您自行編輯 DNS。這是在自動化之前理解其運作機制的最佳方式:
sudo certbot certonly --manual --preferred-challenges dns -d example.com -d '*.example.com'萬用字元外的引號可避免 shell 將 * 視為檔名模式。Certbot 會暫停並顯示指示:
Please deploy a DNS TXT record under the name:
_acme-challenge.example.com.
with the following value:
Jx9mQ2wLr8vTn5cKp0aYdG3hB7fZs4eN1oiRuXqMk6E在 DNS 提供者的管理介面中建立該 TXT 記錄,使用上方的 dig command 確認記錄已可見,然後再按 Enter。由於這次執行會要求裸網域和萬用字元,Certbot 會提示 2 次;在憑證簽發完成前,請保留這 2 筆記錄。成功時,最後會顯示熟悉的訊息:
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem手動模式無法自行續期的原因
每次續期都會重新進行驗證,並產生新的 token,因此 TXT 值每次都會變更。你今天貼上的記錄,到了 60 days 後就沒有作用。續期計時器每天執行兩次 Certbot,且不會有人在鍵盤前貼上新的值,因此手動簽發的憑證會因續期失敗而顯示以下完全相同的錯誤:
Failed to renew certificate example.com with error: The manual plugin is not
working; there may be problems with your existing configuration.
The error was: PluginError('An authentication script must be provided with
--manual-auth-hook when using the manual plugin non-interactively.')你可以撰寫 --manual-auth-hook scripts,呼叫 DNS provider 的 API 來滿足這項要求,但這等於手動重新實作 DNS plugin。你可以使用手動模式了解流程,或用於尚未能自動化 DNS 的網域,進行確實的一次性簽發;請在第 90 day 前設定提醒,因為 Let's Encrypt 已不再寄送到期通知電子郵件。其他情況請使用 plugin。
Ubuntu 24.04 上的外掛路由:certbot-dns-cloudflare
DNS 外掛會保存 DNS 提供者的 API 憑證,並在簽發及每次續期時,自行完成整個 TXT 記錄流程。這裡以 Cloudflare 為例,因為它是大多數人需要的提供者外掛,而且已封裝在 Ubuntu 中。
我們的 Certbot 指南建議在 Ubuntu 24.04 使用 apt 套件,Cloudflare 也不例外:
sudo apt update
sudo apt install certbot python3-certbot-dns-cloudflare先說明版本狀況。24.04 套件庫提供的此外掛版本為 2.0.0,搭配 Certbot 2.9.0;apt policy python3-certbot-dns-cloudflare 會顯示你目前的版本。兩者版本不一致不會造成問題,限定範圍的 API token 也能正常運作,因為 24.04 中的底層 python3-cloudflare 程式庫版本為 2.11.1,高於此外掛支援 token 所需的 2.3.1。較舊的 Ubuntu 版本中,該程式庫版本不足以支援 token,因此你可能會看到網路上警告 apt 外掛會強制使用 Global API Key。這些警告已不適用於 24.04。
請在 Cloudflare 控制台建立限定範圍的 API token,不要使用 Global API Key:依序選取 My Profile、API Tokens、Create Token,設定唯一必要的權限 Zone / DNS / Edit,並限制只能用於你要簽發憑證的那個 zone。將它放在只有 root 能讀取的檔案中:
sudo mkdir -p /root/.secrets
sudo tee /root/.secrets/cloudflare.ini > /dev/null <<'EOF'
dns_cloudflare_api_token = paste_your_scoped_token_here
EOF
sudo chmod 600 /root/.secrets/cloudflare.ini如果檔案可供其他使用者讀取,Certbot 會檢查檔案模式並針對 Unsafe permissions on credentials configuration file 發出警告。現在簽發憑證:
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
-d example.com -d '*.example.com'此外掛會透過 API 建立 TXT 記錄,等待短暫的傳播時間,讓驗證程序執行,然後再次刪除這些記錄。如果你的 zone 名稱伺服器更新變更的速度較慢,請使用 --dns-cloudflare-propagation-seconds 60 延長等待時間。憑證會寫入 /etc/letsencrypt/live/example.com/,接著依照基礎指南的說明,將 nginx 或 Apache 指向 fullchain.pem 與 privkey.pem,並一併設定 deploy hook。
如果您的供應商外掛程式不在 apt
24.04 套件庫只提供少數供應商的外掛程式,其中包括 Cloudflare、Route 53、DigitalOcean,以及通用的 RFC 2136 介面。執行 apt search certbot-dns 查看清單。如果找不到您的供應商,這是唯一需要調整 apt 優先原則的情況:改用 snap 安裝 Certbot 和外掛程式,並先移除 apt 版 Certbot,避免兩個續期計時器同時處理 /etc/letsencrypt:
sudo apt remove certbot python3-certbot-dns-cloudflare
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-yourprovidersnap 外掛程式只能連接 snap 版 Certbot,無法擴充 apt 版 Certbot。因此,兩種安裝不能共存。如果您的 DNS 主機完全不提供 API,實際可行的選項是將網域的 DNS 移至提供 API 的供應商,或自行執行名稱伺服器,再將 rfc2136 外掛程式指向該伺服器。
續期:現在完成驗證,不要等到 60 天後
Certbot 會在 /etc/letsencrypt/renewal/example.com.conf 記錄每張憑證的簽發方式,包括 authenticator = dns-cloudflare 與憑證資料的路徑。因此,標準的每日執行兩次計時器可在不需人工介入的情況下自動續期。請先對 staging environment 完整演練一次:
sudo certbot renew --dry-run測試通過表示憑證資料可正常使用,且驗證已完整完成;60 天後的實際續期會遵循相同流程。今天還應完成以下兩項後續工作。首先,磁碟上的憑證完成續期後,web server 不會自動載入新憑證,因此請依照 nginx 與 Apache 指南中的說明設定 deploy hook。其次,請妥善保護憑證資料檔案:任何能讀取該檔案的人都能修改 DNS zone,足以重新導向郵件,或自行通過 DNS-01 challenge。請將檔案存放於 /root,權限設為 mode 600,將 token 限制在單一 zone;如果懷疑憑證資料曾外洩,請立即輪替 token。
不需要萬用字元時
萬用字元適合用於許多子網域,或無法預先確定的子網域。但對其他情況而言,不應預設使用萬用字元。
- 只有一個子網域,或少數幾個已知子網域:一般 SAN(subject alternative name)憑證較簡單。
certbot --nginx -d example.com -d www.example.com -d app.example.com可透過一般 HTTP-01 驗證涵蓋最多 100 個名稱,而且伺服器上不需要保存 DNS API 憑證。 - 萬用字元只會比對一層標籤。
*.example.com不涵蓋裸網域example.com,因此上面的命令會同時要求這兩者;它也不涵蓋a.b.example.com,後者需要*.b.example.com。 - 每個子網域都使用同一個私密金鑰。如果持有該金鑰的機器遭到入侵,萬用字元涵蓋的所有名稱會同時受到影響。
- 如果由 Traefik 為容器終止 TLS(transport layer security),完全不需要使用 Certbot:Traefik 會自行透過 DNS-01 申請萬用字元憑證,使用同類型的 provider token。
以下情況才真正適合使用萬用字元:每個客戶或應用程式的子網域建立速度快於憑證重新簽發速度,以及沒有公開 80 埠的內部主機,例如只能透過 WireGuard VPN 存取的服務。DNS-01 不會連線到正在驗證的主機,因此即使是完全私有的機器,也能持有受公開信任的憑證。
FAQ
Certbot 可以使用 HTTP-01 核發萬用字元憑證嗎?
不可以。HTTP-01 只能證明對單一主機名稱的控制權,因為驗證伺服器會從該確切名稱擷取 token 檔案。萬用字元憑證涵蓋網域下的所有名稱,因此 Let's Encrypt 要求使用 DNS-01 challenge,而 --nginx、--apache、--webroot 和 --standalone 驗證器全部以 HTTP 為基礎。唯一的方法是在 _acme-challenge.example.com 建立 TXT 記錄,可以手動建立,也可以透過 DNS plugin 建立。
萬用字元憑證涵蓋根網域嗎?
不涵蓋。萬用字元只會比對一個 label,因此 *.example.com 會涵蓋 www.example.com,但不涵蓋裸網域 example.com,也不涵蓋 a.b.example.com。使用 -d example.com -d '*.example.com' 在同一張憑證中要求這兩個名稱。這會建立兩個 challenge,且兩筆 TXT 記錄都位於相同的 _acme-challenge.example.com 名稱,因此請加入第二筆記錄,不要刪除第一筆。
為什麼我的萬用字元憑證不會自動續期?
因為核發時使用了 --manual。每次續期都需要全新的 TXT 值,而 unattended timer 無法貼上這個值,因此續期會停止,並顯示錯誤 An authentication script must be provided with --manual-auth-hook when using the manual plugin non-interactively。請使用 DNS plugin(例如 certbot-dns-cloudflare)重新核發憑證,或提供 --manual-auth-hook 和 --manual-cleanup-hook script,透過供應商的 API 編輯記錄。
_acme-challenge TXT 記錄需要多久才會出現?
這取決於 DNS 供應商,可能只需幾秒,也可能需要數分鐘。驗證會讀取您網域的 authoritative server,因此請使用 dig +short TXT _acme-challenge.example.com @1.1.1.1 檢查,並等待預期值出現後,再繼續手動執行。使用 plugin 時,如果驗證回報找不到記錄,可以透過 plugin 的 propagation 選項提高內建等待時間,例如 --dns-cloudflare-propagation-seconds 60。
萬用字元憑證的安全性低於一般憑證嗎?
加密技術相同。差異在於作業管理:同一把私鑰涵蓋所有子網域,因此一旦私鑰遭到竊取,影響範圍會更大;自動化所需的 DNS API 憑證本身也是敏感 secret,並且會儲存在伺服器上。如果只執行少數已知的子網域,SAN 憑證可以避免這兩項問題。這正是本指南建議不要使用萬用字元憑證的情況。