使用 acme.sh 來申請網站的 SSL 憑證

因協助某專案所遇到的狀況:
某主機放置在防火牆後面,提供 Web 服務,因「資安政策」原因,對外僅開放 443 埠(https) 連線。

所以,目前情況是要在對外連線僅有 443 埠狀況下,申請 SSL 憑證。

要解決這個問題,先了解一下,若只透過 443 埠申請 SSL 有哪些途徑。

  1. DNS-01 驗證 (最推薦)
  2. 目前這是解決防火牆限制的最佳方案。DNS-01 驗證完全不需要開放伺服器的 80 或 443 埠。

    • 原理:
    • ZeroSSL 或 Let’s Encrypt 會要求你在 DNS 紀錄中加入一筆特定的 TXT 紀錄。驗證主機直接檢查你的 DNS 供應商,而非連接你的伺服器。

    • 優點:
    • 伺服器不需要對外開放 80 埠。也可以申請 萬用字元憑證 (Wildcard Certificates)。

    • 作法:
    • 使用支援 DNS API 的工具(如 certbot 搭配外掛程式,或 acme.sh)。

  3. TLS-ALPN-01 驗證
  4. 若無法使用 DNS API,但 443 埠是通的,可以選用 TLS-ALPN-01 驗證。

    • 原理:
    • 這種驗證方式直接透過 443 埠進行 TLS 握手層級的驗證。

    • 限制:
    • 一般的 Certbot 支援度較低,通常需要使用 acme.sh 或內建此功能的 Web Server(如 Caddy 或 Traefik)。

      防火牆必須允許 443 埠的 Inbound (進入) 流量。

    • 作法:
    • 若使用 Caddy Server,它會自動偵測並切換至此模式,完全不需手動干預。

  5. 手動離線申請 (Manual Mode)
  6. 就是真人手動代理人模式,簡單說就是在「另一台」沒有限制的機器上申請憑證。

    在可以連外(80/443 皆通)的主機上執行 certbot certonly –manual,按照指示在 DNS 加入 TXT 紀錄或在目標網站放置驗證檔案。

    在取得憑證檔案(.pem, .key)後,透過 SSH 或其他內部傳輸方式,手動拷貝到該台受限的伺服器上。

    這個方式最大缺點: 每 90 天必須手動更新一次,維護成本極高。

總結
方案 依賴埠口 自動化程度 適用場景
DNS-01 無 (僅需 DNS API) 極高 首選,適合有 API 權限的 DNS 供應商
TLS-ALPN-01 443 Inbound 80 埠被封死但 443 埠可用時
手動模式 萬不得已的最後手段

 

使用 DNS-01 驗證方案

了解之後,決定使用 DNS-01 驗證方案,搭配使用「acme.sh」(簽證到期可自動續簽)工具。

  1. 取得 DNS API 金鑰
  2. 網路主機的 Domain Name 提供商為 Spaceship, 所以到該公司的 API Key 管理頁面:https://www.spaceship.com/zh/application/api-manager/ 去新增與取得 API Key, API Secret

  3. 安裝 acme.sh 與 設定
    1. 安裝 acme.sh:
    2. curl https://get.acme.sh | sh

    3. 設定環境與變數,在 ~/.bashrc 中,新增以下:
    4. ## acme ##
      . "~/.acme.sh/acme.sh.env"

      # 設定環境變數 (acme.sh 會自動記住,下次續期不用再設)
      export SPACESHIP_API_KEY="API Key"
      export SPACESHIP_API_SECRET="API Secret"

    5. 讓環境設定與變數動起來:
    6. source ~/.bashrc

  4. 申請與安裝憑證
  5. acme.sh 憑證申請,預設是向 ZeroSSL 申請。

    但在申請的時候,等待對方回應時間太長,所以憑證申請一直失敗,因此決定改向 Let’s Encrypt 申請。

    而且, Let’s Encrypt 憑證有效期間是 90 天,而 ZeroSSL 憑證有效期間是 60 天。所以,還是向 Let’s Encrypt 申請憑證。

    1. 更新版本,先確定 acms.sh 為最新版本
    2. acme.sh --upgrade

    3. 更換 CA 機構:
    4. acme.sh --set-default-ca --server Letsencrypt

    5. 申請憑證:
    6. acme.sh --issue --dns dns_spaceship -d example.com -d *.example.com

    7. 跑完申請憑證後,可以看一下是否成功(有點成就感)
    8. acme.sh --list

    9. 先建個目錄以便安放憑證:
    10. sudo mkdir -p /etc/acme_ssl/

    11. 安裝憑證(注意並調整一下目錄權限):
    12. acme.sh --install-cert -d example.com \
      --cert-file /etc/acme_ssl/example.com.crt \
      --key-file /etc/acml_ssl/example.com.key \
      --fullchain-file /etc/acme_ssl/example.com_fullchain.pem \
      --reloadcmd "sudo systemctl reload apache2"

  6. 設定好憑證檔案位置
  7. 因為主機是使用 apache2,所以相關檔案應該是在 default-ssl.conf 上。確認與注意以下設定:

    • SSLEngine on
    • SSLCertificateFile /etc/acme_ssl/example.com.crt
    • SSLCertificateKeyFile /etc/acme_ssl/example.com.key
    • SSLCertificateChainFile /etc/acme_ssl/example.com_fullchain.pem

    確認沒問題後,重啟 apache2 服務,sudo systemctl reload apache2

  8. 如何確認到期前會自動續簽?
    • 執行 crontab -l
    • 看有無包含 acme.sh –cron 命令,若無,執行 acme.sh –install-cronjob

    • 檢查憑證清單
    • 執行 acme.sh –list 便可以看到詳細資訊。
      例如:

    • 測試模擬續簽,強制執行續簽檢查一下
    • 執行 acme.sh –cron -f

 

使用 TLS-ALPN-01 驗證方案

作法和步驟和上面幾乎相同,只有申請憑證的語法不同,而且不支援 *.example.com ,僅能申請單一網域

  • 停止佔用 443 埠的程式,例如 apache 或 nginx
  • sudo systemctl stop apache2

  • 申請憑證語法:
  • acme.sh --issue --alpn -d example.com

  • 註:
  • 理論上可行,但在此網路主機實際執行,卻一再失敗。
    錯誤訊息顯示,「可能被防火牆擋了」。

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

*