用 Shell 腳本自動申請與續期 SSL 憑證 文章首圖

用 Shell 腳本自動申請與續期 SSL 憑證

用 Shell 腳本自動申請與續期 SSL 憑證

在現代 Web 開發中,HTTPS 已不再是選項,而是標配。雖然 Let's Encrypt 提供了免費且自動化的憑證簽發服務,但如何將申請、部署與續期過程自動化,仍是許多系統管理員的痛點。本文将介紹如何使用 Shell 腳本搭配 certbot,在 Ubuntu 22.04 或 Debian 12 上實現全自動化的 SSL 憑證管理。

環境準備與依賴安裝

首先,確保您的系統已安裝必要的軟體。在 Debian/Ubuntu 系發行版中,certbot 是官方推薦的 ACME 客戶端。

sudo apt update
sudo apt install -y certbot python3-certbot-nginx

註:若您使用 Apache,請安裝 python3-certbot-apache;若僅需獲取憑證檔案而不直接配置 Web 伺服器,可僅安裝 certbot

自動化腳本設計思路

為了避免手動干預,我們需要一個腳本完成以下任務:

  1. 檢查憑證是否即將過期(例如剩餘 30 天內)。
  2. 若需續期,自動執行申請流程。
  3. 重新載入 Web 伺服器以套用新憑證。
  4. 記錄日誌以便追蹤。

以下是一個完整且可執行的 Shell 腳本範例:

完整 Shell 腳本範例

建立檔案 /usr/local/bin/auto-renew-ssl.sh

#!/bin/bash

# 設定變數
DOMAIN="example.com"  # 請替換為您要保護的網域
EMAIL="admin@example.com"  # 請替換為您的聯絡信箱
CERT_PATH="/etc/letsencrypt/live/${DOMAIN}/fullchain.pem"
KEY_PATH="/etc/letsencrypt/live/${DOMAIN}/privkey.pem"
NGINX_CONF="/etc/nginx/sites-available/${DOMAIN}"
LOG_FILE="/var/log/ssl-renewal.log"

# 函式:記錄日誌
log_message() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE"
}

# 檢查憑證剩餘天數
check_cert_expiry() {
    if [ ! -f "$CERT_PATH" ]; then
        log_message "錯誤:憑證檔案不存在,將執行首次申請。"
        return 1
    fi

    # 使用 openssl 查詢憑證到期日
    EXPIRY_DATE=$(openssl x509 -enddate -noout -in "$CERT_PATH" | cut -d= -f2)
    EXPIRY_TIMESTAMP=$(date -d "$EXPIRY_DATE" +%s)
    CURRENT_TIMESTAMP=$(date +%s)

    # 計算剩餘天數
    DAYS_LEFT=$(( (EXPIRY_TIMESTAMP - CURRENT_TIMESTAMP) / 86400 ))

    log_message "憑證剩餘天數:${DAYS_LEFT} 天"

    if [ "$DAYS_LEFT" -lt 30 ]; then
        return 0  # 需要續期
    else
        return 1  # 不需處理
    fi
}

# 執行續期或申請
renew_or_issue() {
    log_message "開始申請/續期憑證..."

    # 使用 certbot 申請憑證,並設定 webroot 模式以兼容非標準配置
    # --dry-run 可用於測試,正式環境請移除
    certbot certonly --webroot \
        -w /var/www/html \
        -d "${DOMAIN}" \
        --email "${EMAIL}" \
        --agree-tos \
        --no-eff-email \
        --force-renewal \
        --non-interactive 2>&1 | tee -a "$LOG_FILE"

    if [ $? -eq 0 ]; then
        log_message "憑證申請成功!"
        # 重新載入 Nginx
        nginx -t && systemctl reload nginx
        log_message "Nginx 已重新載入。"
    else
        log_message "錯誤:憑證申請失敗,請檢查日誌。"
        exit 1
    fi
}

# 主程式流程
main() {
    log_message "=== SSL 憑證檢查開始 ==="

    if check_cert_expiry; then
        renew_or_issue
    else
        log_message "憑證狀態良好,無需處理。"
    fi

    log_message "=== SSL 憑證檢查結束 ==="
}

# 執行主程式
main

腳本使用說明

  1. 修改變數:請務必將 DOMAINEMAILCERT_PATH 等變數替換為您的實際環境資訊。
  2. 權限設定
    sudo chmod +x /usr/local/bin/auto-renew-ssl.sh
  3. 手動測試
    sudo /usr/local/bin/auto-renew-ssl.sh

    觀察 /var/log/ssl-renewal.log 輸出,確認無錯誤。

設定 Cron 定時任務

為了實現自動化,我們將腳本加入 Crontab。建議每 12 小時執行一次,讓系統有足夠時間在憑證過期前觸發續期。

sudo crontab -e

在檔案末尾加入以下行:

0 */12 * * * /usr/local/bin/auto-renew-ssl.sh >> /var/log/ssl-cron.log 2>&1

常見問題與解決方案

1. 憑證申請失敗,提示 403 或 404 錯誤

這通常與 Webroot 路徑配置錯誤有關。Certbot 會透過 http://domain/.well-known/acme-challenge/ 驗證域名所有權。請確保:

  • Nginx 的 server block 中,root 指向的目錄確實存在且可寫。
  • 防火牆允許 HTTP (80) 和 HTTPS (443) 流量。
  • 若使用 CDN,需確認 CDN 支援 ACME 驗證或暫時關閉 CDN。

2. 憑證續期後 Web 伺服器未更新

部分服務(如 Nginx、Apache)在憑證檔案內容變更時,不會自動重新讀取。因此,腳本中必須包含重新載入服務的指令(如 systemctl reload nginx)。若您的應用是靜態網頁或需手動觸發的後端服務,請在腳本中補充相應的觸發機制。

小結

透過撰寫簡單的 Shell 腳本並結合 certbotcron,我們可以輕鬆建立一套可靠的 SSL 憑證自動化管理系統。這不僅消除了手動續期的遺忘風險,也確保了網站始終處於安全的 HTTPS 狀態。對於更複雜的架構,建議進一步研究 certbot 的 hook 參數,以支援更靈活的部署流程。