TLS Certificates for Internal Services: Split-Horizon DNS and ACME Implementation

內部服務的 TLS 憑證:分割視域 DNS 與 ACME 實作

內部 TLS 的挑戰

為內部服務加上 TLS 通常只能在兩種方案間做二選一:使用自簽憑證或使用公證憑證。自簽憑證可以避免洩漏內部網路資訊,但必須讓每一台客戶端裝置手動信任自訂的根 CA,這在管理上相當繁重。公證憑證則預設受信任,但通常需要服務在公共互聯網上可達,以便完成驗證。

為內部服務實作分割視域 DNS

分割視域 DNS 允許同一個域名根據請求者的位置解析到不同的 IP 位址。對於內部服務而言,這意味著像 grafana.tuxnet.dev 這樣的域名會對外部解析器返回公共 IP(以滿足 CA 驗證),而對 VPN 內部的客戶端則返回私有內部 IP。

技術架構

要實作此工作流程,需要以下元件:

  1. 具 DNS 解析功能的 VPN:可使用 NetBird 之類的工具管理自訂區域,確保 VPN 連線的客戶端取得內部 IP,而伺服器本身則使用公共 DNS 以便簽發憑證。
  2. ACME 客戶端acme.sh 可用於自動向 Let's Encrypt 或 ZeroSSL 申請憑證。
  3. 反向代理 / WAF:Nginx 可作為入口點,僅綁定在 VPN 網路介面上,確保只有來自 VPN 的流量能存取內部服務,充當 Web Access Firewall(WAF)。

憑證簽發與續期

在獨立模式下使用 acme.shhttp-01 挑戰,讓客戶端僅在簽發過程中佔用 80 埠,這樣 Nginx 就不必永久佔用 80 埠。

為了自動續期,可設定 cron 工作執行 acme.sh --cron,然後將憑證同步至 Nginx 目錄並重新載入服務。為避免以 root 身份執行 ACME 客戶端,可對 socat 設定 setcap CAP_NET_BIND_SERVICE=+ep,讓非特權使用者也能綁定 80 埠。

使用 SAN 與 CNAME 進行擴展

與其為每個內部服務各自簽發憑證,不如使用主體備選名稱(SAN)。只要建立一張包含多個 DNS 名稱(例如 internal.tuxnet.devgrafana.tuxnet.devanalytics.tuxnet.dev)的憑證,即可在多個 Nginx server block 中共用同一憑證檔案。

替代方案與取捨

雖然分割視域 DNS 解決了客戶端信任的問題,但許多工程師仍提出其他方法,以避免「分裂大腦」的 DNS 行為與內部主機名稱的洩漏。

DNS-01 驗證

許多實務者建議使用 DNS-01 挑戰取代 HTTP-01。DNS-01 驗證允許 CA 透過公共 DNS 中的 TXT 記錄驗證域名所有權,這意味著內部服務根本不需要公開可路由或擁有公共 IP 位址,從而完全不需要分割視域 DNS。

通配符憑證

通配符憑證(*.domain.com)常被用來避免將特定內部子域名洩漏至憑證透明度(CT)日誌。雖然有人認為若私鑰外洩,通配符憑證的安全風險較高,但也有人認為在內部基礎設施中,單一負載平衡器處理所有終端的情況下,使用通配符更為實用。

內部 CA 與 mTLS

在高安全性環境下,通常會使用內部 CA(例如 step-ca),確保內部主機名稱永不出現在公共 CT 日誌中。此外,實作雙向 TLS(mTLS)可提供比標準 TLS 更強的身份保證,因為客戶端必須向伺服器出示憑證,將安全性從網路層(VPN)提升到身份層。

比較摘要

方法 客戶端信任 DNS 複雜度 隱私(CT 日誌) 網路暴露
分割視域 自動 低(會洩漏名稱) 低(透過 WAF)
DNS-01 自動 低(會洩漏名稱)
內部 CA 手動/MDM 高(私有)
mTLS 手動/MDM 高(私有)

摘要

使用分割視域 DNS 與 ACME 為內部服務實作 TLS,可在不需要客戶端信任自簽根 CA 的情況下取得公證 CA 簽發的憑證,但同時也帶來 DNS 複雜度與憑證透明度日誌方面的取捨。


TLS Certificates for Internal Services: Split-Horizon DNS and ACME Implementation

Sources