憑證過期監控與告警系統

Certificate Expiry Monitoring and Alerting System

前言

在現代 IT 基礎設施中,SSL/TLS 憑證是確保通訊安全的關鍵元件。然而,憑證過期往往是造成服務中斷的常見原因之一。本文將介紹如何建立一套完整的憑證過期監控與告警系統,確保您的服務不會因為憑證過期而中斷。

憑證監控的重要性

為什麼需要監控憑證?

  1. 避免服務中斷:憑證過期會導致 HTTPS 連線失敗,用戶將看到安全警告
  2. 維護品牌信譽:憑證錯誤會降低用戶對網站的信任度
  3. 合規要求:許多產業法規要求持續維護有效的加密憑證
  4. 自動化管理:隨著微服務架構普及,手動追蹤數百個憑證已不切實際

常見的憑證問題

  • 憑證已過期
  • 憑證即將過期(通常 30 天內需要關注)
  • 憑證鏈不完整
  • 憑證與網域名稱不符

使用 OpenSSL 檢查憑證到期日

基本指令

檢查遠端伺服器憑證:

1
2
3
4
5
6
7
# 檢查網站憑證到期日
echo | openssl s_client -servername example.com -connect example.com:443 2>/dev/null | \
  openssl x509 -noout -dates

# 輸出範例:
# notBefore=Jan  1 00:00:00 2024 GMT
# notAfter=Dec 31 23:59:59 2024 GMT

計算剩餘天數

1
2
3
4
5
6
7
8
9
# 取得憑證到期日並計算剩餘天數
DOMAIN="example.com"
EXPIRY_DATE=$(echo | openssl s_client -servername $DOMAIN -connect $DOMAIN:443 2>/dev/null | \
  openssl x509 -noout -enddate | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY_DATE" +%s)
CURRENT_EPOCH=$(date +%s)
DAYS_LEFT=$(( ($EXPIRY_EPOCH - $CURRENT_EPOCH) / 86400 ))

echo "憑證將在 $DAYS_LEFT 天後過期"

批次檢查腳本

以下腳本可以批次檢查多個網域的憑證狀態:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#!/bin/bash
# cert_check.sh - 批次憑證檢查腳本

# 設定告警閾值(天數)
WARNING_DAYS=30
CRITICAL_DAYS=7

# 要檢查的網域清單
DOMAINS=(
    "example.com"
    "api.example.com"
    "admin.example.com"
    "www.example.com"
)

# 顏色定義
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color

check_certificate() {
    local domain=$1
    local port=${2:-443}

    # 取得憑證到期日
    expiry_date=$(echo | openssl s_client -servername "$domain" \
        -connect "$domain:$port" 2>/dev/null | \
        openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)

    if [ -z "$expiry_date" ]; then
        echo -e "${RED}[ERROR]${NC} $domain - 無法取得憑證資訊"
        return 1
    fi

    # 計算剩餘天數
    expiry_epoch=$(date -d "$expiry_date" +%s)
    current_epoch=$(date +%s)
    days_left=$(( ($expiry_epoch - $current_epoch) / 86400 ))

    # 根據剩餘天數顯示不同狀態
    if [ $days_left -lt $CRITICAL_DAYS ]; then
        echo -e "${RED}[CRITICAL]${NC} $domain - 剩餘 $days_left 天 (到期: $expiry_date)"
    elif [ $days_left -lt $WARNING_DAYS ]; then
        echo -e "${YELLOW}[WARNING]${NC} $domain - 剩餘 $days_left 天 (到期: $expiry_date)"
    else
        echo -e "${GREEN}[OK]${NC} $domain - 剩餘 $days_left 天 (到期: $expiry_date)"
    fi
}

echo "=========================================="
echo "憑證到期檢查報告 - $(date '+%Y-%m-%d %H:%M:%S')"
echo "=========================================="

for domain in "${DOMAINS[@]}"; do
    check_certificate "$domain"
done

Prometheus 與 ssl_exporter

安裝 ssl_exporter

ssl_exporter 是專門用於監控 SSL 憑證的 Prometheus exporter。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 下載並安裝 ssl_exporter
wget https://github.com/ribbybibby/ssl_exporter/releases/download/v2.4.2/ssl_exporter-2.4.2.linux-amd64.tar.gz
tar xvzf ssl_exporter-2.4.2.linux-amd64.tar.gz
sudo mv ssl_exporter-2.4.2.linux-amd64/ssl_exporter /usr/local/bin/

# 建立 systemd 服務
sudo tee /etc/systemd/system/ssl_exporter.service > /dev/null <<EOF
[Unit]
Description=SSL Certificate Exporter
After=network.target

[Service]
Type=simple
User=prometheus
ExecStart=/usr/local/bin/ssl_exporter
Restart=always

[Install]
WantedBy=multi-user.target
EOF

# 啟動服務
sudo systemctl daemon-reload
sudo systemctl enable ssl_exporter
sudo systemctl start ssl_exporter

Prometheus 設定

prometheus.yml 中加入以下設定:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
scrape_configs:
  - job_name: 'ssl'
    metrics_path: /probe
    static_configs:
      - targets:
          - example.com:443
          - api.example.com:443
          - admin.example.com:443
    relabel_configs:
      - source_labels: [__address__]
        target_label: __param_target
      - source_labels: [__param_target]
        target_label: instance
      - target_label: __address__
        replacement: localhost:9219  # ssl_exporter 位址

重要的監控指標

ssl_exporter 提供以下關鍵指標:

  • ssl_cert_not_after - 憑證到期時間戳記
  • ssl_cert_not_before - 憑證生效時間戳記
  • ssl_verified_cert_not_after - 已驗證憑證鏈的到期時間

Grafana 儀表板

建立憑證監控儀表板

以下是 Grafana 儀表板的 JSON 設定片段:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
{
  "panels": [
    {
      "title": "憑證剩餘天數",
      "type": "gauge",
      "targets": [
        {
          "expr": "(ssl_cert_not_after - time()) / 86400",
          "legendFormat": "{{instance}}"
        }
      ],
      "fieldConfig": {
        "defaults": {
          "thresholds": {
            "steps": [
              { "value": 0, "color": "red" },
              { "value": 7, "color": "orange" },
              { "value": 30, "color": "yellow" },
              { "value": 60, "color": "green" }
            ]
          },
          "unit": "d"
        }
      }
    }
  ]
}

常用 PromQL 查詢

1
2
3
4
5
6
7
8
# 計算憑證剩餘天數
(ssl_cert_not_after - time()) / 86400

# 找出 30 天內即將過期的憑證
(ssl_cert_not_after - time()) / 86400 < 30

# 憑證到期時間表格
ssl_cert_not_after * 1000

AlertManager 告警設定

告警規則

建立 /etc/prometheus/rules/ssl_alerts.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
groups:
  - name: ssl_certificate_alerts
    rules:
      - alert: SSLCertificateExpiringSoon
        expr: (ssl_cert_not_after - time()) / 86400 < 30
        for: 1h
        labels:
          severity: warning
        annotations:
          summary: "SSL 憑證即將過期"
          description: "{{ $labels.instance }} 的憑證將在 {{ $value | printf \"%.0f\" }} 天內過期"

      - alert: SSLCertificateCritical
        expr: (ssl_cert_not_after - time()) / 86400 < 7
        for: 1h
        labels:
          severity: critical
        annotations:
          summary: "SSL 憑證即將過期 - 緊急"
          description: "{{ $labels.instance }} 的憑證將在 {{ $value | printf \"%.0f\" }} 天內過期,請立即處理!"

      - alert: SSLCertificateExpired
        expr: (ssl_cert_not_after - time()) < 0
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "SSL 憑證已過期"
          description: "{{ $labels.instance }} 的憑證已經過期!"

AlertManager 通知設定

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# alertmanager.yml
global:
  slack_api_url: 'https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK'

route:
  group_by: ['alertname']
  group_wait: 10s
  group_interval: 10m
  repeat_interval: 1h
  receiver: 'slack-notifications'
  routes:
    - match:
        severity: critical
      receiver: 'pagerduty-critical'

receivers:
  - name: 'slack-notifications'
    slack_configs:
      - channel: '#alerts'
        title: '{{ .GroupLabels.alertname }}'
        text: '{{ range .Alerts }}{{ .Annotations.description }}{{ end }}'

  - name: 'pagerduty-critical'
    pagerduty_configs:
      - service_key: YOUR_PAGERDUTY_KEY

雲端解決方案

AWS Certificate Manager (ACM)

ACM 提供自動續約功能,搭配 CloudWatch 進行監控:

1
2
3
4
5
6
7
# 使用 AWS CLI 列出所有憑證
aws acm list-certificates --region ap-northeast-1

# 取得憑證詳細資訊
aws acm describe-certificate \
  --certificate-arn arn:aws:acm:ap-northeast-1:123456789:certificate/xxx \
  --query 'Certificate.NotAfter'

Azure Key Vault

1
2
3
4
5
6
7
8
# 列出所有憑證
az keyvault certificate list --vault-name MyKeyVault

# 取得憑證到期日
az keyvault certificate show \
  --vault-name MyKeyVault \
  --name MyCertificate \
  --query 'attributes.expires'

自動化續約整合

結合 Certbot 自動續約

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#!/bin/bash
# /etc/cron.weekly/cert-renew-check.sh

# 檢查並續約憑證
certbot renew --quiet

# 重新載入服務
systemctl reload nginx

# 發送通知
if [ $? -eq 0 ]; then
    curl -X POST -H 'Content-type: application/json' \
      --data '{"text":"憑證續約檢查完成"}' \
      https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK
fi

使用 cert-manager (Kubernetes)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: example-com-tls
  namespace: default
spec:
  secretName: example-com-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
    - example.com
    - www.example.com
  renewBefore: 360h  # 到期前 15 天續約

最佳實踐

監控策略建議

  1. 設定多層告警閾值

    • 60 天:資訊通知
    • 30 天:警告通知
    • 7 天:緊急通知
    • 已過期:立即通知 + 呼叫值班人員
  2. 建立憑證清單

    • 維護所有憑證的集中清單
    • 記錄憑證用途、負責人、到期日
  3. 自動化續約

    • 盡可能使用自動續約機制
    • 對於無法自動續約的憑證,提前安排更新時間
  4. 定期審查

    • 每季度審查一次憑證狀態
    • 移除不再使用的憑證
    • 更新監控清單

監控檢查清單

  • 所有對外服務的 HTTPS 憑證
  • 內部服務的 TLS 憑證
  • 中繼憑證(Intermediate CA)
  • 程式碼簽章憑證
  • API 客戶端憑證

參考資料

結語

建立完善的憑證監控系統是維護服務可用性的重要環節。透過本文介紹的工具和方法,您可以有效地監控所有憑證的狀態,並在問題發生前及時處理。建議從簡單的腳本開始,逐步導入 Prometheus 等完整的監控方案,最終實現憑證管理的完全自動化。

comments powered by Disqus
Built with Hugo
Theme Stack designed by Jimmy