為什麼選擇 TWCA OV 證書

在生產環境中,使用自簽憑證會導致瀏覽器顯示不安全警告,影響使用者信任。雖然 Let’s Encrypt 提供免費的自動化證書,但某些企業或政府專案需要台灣在地的認證機構簽發證書以符合法規要求。

SSL 證書的三個等級

SSL 證書依據驗證強度分為三個等級:

證書類型驗證內容適用場景信任標記價格
DV (Domain Validation)僅驗證網域所有權個人網站、部落格🔒 基本鎖頭免費~低
OV (Organization Validation)驗證組織身份企業網站、SaaS 服務🔒 組織名稱
EV (Extended Validation)嚴格驗證企業金融、電商、政府🟢 綠色網址列

本文使用的是 TWCA OV SSL 證書,它提供:

  • ✅ 組織身份驗證(瀏覽器可顯示公司名稱)
  • ✅ 完整的證書鏈(受所有主流瀏覽器信任)
  • ✅ 12 個月有效期
  • ✅ 台灣在地技術支援

HTTPS 與 SSL/TLS 運作原理

在深入部署之前,先理解 HTTPS 如何保護資料傳輸:

sequenceDiagram
    participant Browser as 瀏覽器<br/>(內建 TWCA 根憑證)
    participant Server as Web Server

    Note over Browser,Server: 1. TLS 握手階段
    Browser->>Server: ClientHello (支援的加密套件)
    Server->>Browser: ServerHello (選擇的加密套件)
    Server->>Browser: 傳送證書鏈<br/>(伺服器證書 + 中繼憑證)

    Note over Browser: 2. 證書驗證(完全離線)
    Browser->>Browser: 用中繼憑證驗證伺服器證書
    Browser->>Browser: 用內建根憑證驗證中繼憑證
    Browser->>Browser: 檢查證書有效期與網域
    Note over Browser: ✓ 證書有效

    Note over Browser,Server: 3. 金鑰交換
    Browser->>Server: 用公鑰加密的 session key
    Server->>Server: 用私鑰解密 session key

    Note over Browser,Server: 4. 加密通訊
    Browser->>Server: 加密的 HTTP 請求
    Server->>Browser: 加密的 HTTP 回應

關鍵機制說明:

  1. 非對稱加密(RSA/ECDSA):只用於金鑰交換,確保 session key 安全傳輸
  2. 對稱加密(AES):實際資料傳輸使用,效能更好
  3. 證書鏈驗證(完全離線):瀏覽器使用內建的 TWCA 根憑證驗證整個證書鏈,不需要連線到 TWCA
  4. 中間人攻擊防護:因為攻擊者沒有伺服器的私鑰,無法解密通訊

💡 重要觀念: TWCA 只在簽發證書時參與,TLS 握手過程中完全不涉及。瀏覽器使用內建的根憑證庫(包含 TWCA 根憑證)進行離線驗證。

證書鏈驗證的實際過程

┌──────────────────────────────────────────────┐
│ 瀏覽器(內建根憑證庫)                        │
├──────────────────────────────────────────────┤
│                                              │
│  Server 傳來的證書鏈:                        │
│  ┌───────────────────────────────┐          │
│  │ ① www.myCompany.com 證書        │ ← Server │
│  │   (由 TWCA 中繼憑證簽發)       │          │
│  └───────────┬───────────────────┘          │
│              ↓ 用中繼憑證公鑰驗證             │
│  ┌───────────────────────────────┐          │
│  │ ② TWCA Secure SSL CA 中繼憑證 │ ← Server │
│  │   (由 TWCA 根憑證簽發)         │          │
│  └───────────┬───────────────────┘          │
│              ↓ 用根憑證公鑰驗證               │
│  ┌───────────────────────────────┐          │
│  │ ③ TWCA Root CA 根憑證         │ ← 瀏覽器 │
│  │   (內建信任清單)               │   內建   │
│  └───────────────────────────────┘          │
│              ✓ 信任鏈完整                    │
└──────────────────────────────────────────────┘

TWCA 證書申請與 DNS 驗證

申請流程

TWCA 採用 ACME (Automatic Certificate Management Environment) 協議進行網域驗證。申請證書時,你需要:

  1. 準備 CSR (Certificate Signing Request):包含網域名稱、組織資訊和公鑰
  2. 進行 DNS TXT 記錄驗證:證明你擁有網域控制權

Route53 在驗證中的角色

AWS Route53 是托管 DNS 服務,在 SSL 證書驗證流程中扮演關鍵角色:

flowchart LR
    A[TWCA 驗證伺服器] -->|1. DNS 查詢| B[Route53]
    B -->|2. 返回 TXT 記錄| A
    A -->|3. 驗證成功| C[簽發證書]

    style B fill:#ff9900,color:#000
    style C fill:#00a86b,color:#000

Route53 提供的優勢:

  • API 整合:可透過 AWS CLI 或 SDK 自動化 DNS 記錄管理
  • 即時生效:記錄變更通常在 60 秒內全球傳播
  • 高可用性:99.99% SLA 保證
  • 安全性:支援 DNSSEC 和 IAM 權限控制

DNS 驗證實作

TWCA 會提供一個驗證字串,你需要在 DNS 中建立 TXT 記錄:

# 使用 AWS Route53 建立 ACME 驗證記錄
aws route53 change-resource-record-sets \
  --hosted-zone-id Z01234567XOQOJYWWWUAC \
  --change-batch file://dns-challenge.json

驗證記錄格式範例:

{
  "Comment": "ACME DNS challenge for TWCA OV SSL",
  "Changes": [{
    "Action": "CREATE",
    "ResourceRecordSet": {
      "Name": "_acme-challenge.backend.myCompany.com.",
      "Type": "TXT",
      "TTL": 300,
      "ResourceRecords": [{
        "Value": "\"Kg7PqRsT9uVwXyZaBCdEfGhIjKlMnOpQ2rS3tU4vW\""
      }]
    }
  }]
}

為什麼使用 DNS 驗證?

  • 適合 Kubernetes 環境:不需要在容器內部署驗證檔案
  • 支援萬用字元證書:如 *.myCompany.com
  • 自動化友善:可整合到 CI/CD 流程
  • 安全性高:驗證過程不暴露伺服器資訊

驗證記錄建立後,確認 DNS 已生效:

# 驗證 DNS 記錄是否可查詢
dig _acme-challenge.backend.myCompany.com TXT +short

# 預期輸出
"Kg7PqRsT9uVwXyZaBCdEfGhIjKlMnOpQ2rS3tU4vW"

TWCA 驗證通過後,即可下載證書檔案(certificate.crt)和中繼憑證(ca-bundle.crt)。

Kubernetes TLS Secret 深度解析

什麼是 TLS Secret?

Kubernetes Secret 是一種用於儲存敏感資料的資源物件。TLS Secret 是 Secret 的特殊類型,專門用來儲存 SSL/TLS 證書。

Secret 的底層機制:

flowchart TD
    A[TLS Secret] -->|包含| B[tls.crt<br/>證書 + 證書鏈]
    A -->|包含| C[tls.key<br/>私鑰]

    B -->|掛載到| D[Ingress Controller Pod]
    C -->|掛載到| D

    D -->|使用| E[Nginx<br/>SSL 終止]

    style A fill:#326ce5
    style D fill:#ff6b6b,color:#000
    style E fill:#4ecdc4,color:#000

為什麼使用 Secret?

  1. 加密儲存:Secret 在 etcd 中可以加密儲存(需啟用 encryption at rest)
  2. 存取控制:透過 RBAC 限制誰能讀取 Secret
  3. 自動掛載:Ingress Controller 自動讀取並套用證書
  4. 版本控制:更新 Secret 後,Ingress 自動重載新證書

建立 TLS Secret

# 合併證書鏈(重要!)
cat certificate.crt ca-bundle.crt > fullchain.crt

# 建立 TLS Secret
kubectl create secret tls backend-tls-secret \
  --cert=fullchain.crt \
  --key=private.key \
  -n default

為什麼要合併證書鏈?

瀏覽器                伺服器證書            中繼憑證             根憑證
  │                     │                    │                   │
  ├─────驗證──────────-─→│                    │                   │
  │                     ├─────由誰簽發?──────→│                   │
  │                     │                    ├─────由誰簽發?──-─→│
  │                     │                    │                   │
  │←────信任鏈完整────-──┴────────────────────┴──────(根憑證在瀏覽器內建)

如果缺少中繼憑證,部分瀏覽器(尤其是移動裝置)會顯示「證書不受信任」。

驗證 Secret 建立成功:

# 檢查 Secret 內容
kubectl get secret backend-tls-secret -n default -o yaml

# 解碼並檢查證書資訊
kubectl get secret backend-tls-secret -n default \
  -o jsonpath='{.data.tls\.crt}' | \
  base64 -d | \
  openssl x509 -noout -text | \
  grep -E "(Issuer:|Subject:|Not After)"

預期輸出:

Issuer: C=TW, O=TAIWAN-CA, CN=TWCA Secure SSL Certification Authority
Subject: C=TW, ST=Taiwan, L=Taipei, O=ITCompany Inc., CN=backend.myCompany.com
Not After : Apr 28 15:59:59 2026 GMT

Kubernetes Ingress 部署

Ingress 資源配置

Ingress 是 Kubernetes 中負責 HTTP/HTTPS 流量路由的資源。我們需要將 TLS Secret 綁定到 Ingress 上:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myCompany-strapi-ingress
  namespace: default
  annotations:
    # SSL 強制重定向
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"

    # 檔案上傳大小限制
    nginx.ingress.kubernetes.io/proxy-body-size: "4096m"

    # 啟用 HSTS(下方詳細說明)
    nginx.ingress.kubernetes.io/configuration-snippet: |
      add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - backend.myCompany.com
    secretName: backend-tls-secret  # 引用 TLS Secret
  rules:
  - host: backend.myCompany.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: myCompany-strapi-service-prod
            port:
              number: 80

套用配置:

kubectl apply -f ingress.yaml

# 檢查 Ingress 狀態
kubectl get ingress myCompany-strapi-ingress -n default

SSL 強制重定向機制

Nginx Ingress Controller 會自動處理 HTTP 到 HTTPS 的跳轉:

flowchart LR
    A[使用者訪問<br/>http://backend.myCompany.com] -->|HTTP 請求| B[Nginx Ingress]
    B -->|檢查 annotations| C{force-ssl-redirect?}
    C -->|是| D[返回 308<br/>Permanent Redirect]
    D -->|重定向到| E[https://backend.myCompany.com]
    E -->|建立 TLS 連線| F[加密通訊]

    style D fill:#ffd93d,color:#000
    style F fill:#6bcf7f,color:#000

為什麼使用 308 而非 301?

狀態碼說明HTTP 方法保留用途
301Moved Permanently❌ POST → GET網址永久變更
302Found (臨時重定向)❌ POST → GET暫時性跳轉
308Permanent Redirect✅ POST 保持 POSTSSL 強制重定向

使用 308 確保 POST 請求不會被轉換成 GET,避免資料遺失。

HSTS:強化 HTTPS 安全性

什麼是 HSTS?

HSTS (HTTP Strict Transport Security) 是一種安全機制,強制瀏覽器只使用 HTTPS 連線,防止降級攻擊。

沒有 HSTS 的風險:

sequenceDiagram
    participant User as 使用者
    participant Attacker as 攻擊者
    participant Server as 伺服器

    User->>Attacker: http://backend.myCompany.com
    Note over Attacker: 攻擊者攔截 HTTP 請求
    Attacker->>User: 假造的 HTTP 回應
    Note over User: 敏感資料外洩!

啟用 HSTS 後:

sequenceDiagram
    participant User as 使用者
    participant Browser as 瀏覽器
    participant Server as 伺服器

    User->>Browser: 訪問 backend.myCompany.com
    Note over Browser: 檢查 HSTS 清單
    Browser->>Browser: 自動升級為 HTTPS
    Browser->>Server: https://backend.myCompany.com
    Server->>Browser: Strict-Transport-Security 標頭
    Note over Browser: 記住:未來 1 年都用 HTTPS

HSTS 標頭解析

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

參數說明:

  • max-age=31536000:有效期 1 年(秒數)
  • includeSubDomains:套用到所有子網域(如 api.myCompany.com
  • preload:可申請加入瀏覽器內建的 HSTS 清單

⚠️ 警告: 啟用 HSTS 後,如果證書過期或配置錯誤,使用者將完全無法訪問網站(瀏覽器會拒絕連線)。請確保證書自動更新機制正常運作。

AWS 提供的自動化工具

AWS Certificate Manager (ACM)

如果你的服務運行在 AWS Load Balancer 後方,可以使用 ACM 免費簽發證書:

優勢:

  • ✅ 完全免費(DV 證書)
  • ✅ 自動更新(永不過期)
  • ✅ 與 ELB/ALB 無縫整合

限制:

  • ❌ 證書只能用於 AWS 服務(無法匯出私鑰)
  • ❌ 不支援 OV/EV 證書
  • ❌ 必須使用 AWS Load Balancer

cert-manager:Kubernetes 自動化證書管理

cert-manager 是 Kubernetes 生態中的證書管理工具,可自動化申請和更新證書:

flowchart TD
    A[cert-manager Controller] -->|監控| B[Certificate 資源]
    B -->|需要更新?| C{證書到期?}
    C -->|是| D[發起 ACME Challenge]
    D -->|建立| E[DNS TXT 記錄<br/>透過 Route53]
    E -->|驗證| F[Let's Encrypt]
    F -->|簽發| G[新證書]
    G -->|更新| H[TLS Secret]
    H -->|通知| I[Ingress Controller]
    I -->|重載| J[新證書生效]

    style A fill:#326ce5
    style F fill:#00a86b
    style H fill:#ff6b6b

安裝 cert-manager:

# 安裝 cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml

# 配置 Route53 DNS 驗證
kubectl create secret generic route53-credentials \
  --from-literal=secret-access-key=$AWS_SECRET_ACCESS_KEY \
  -n cert-manager

建立 Certificate 資源(自動更新):

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: backend-cert
  namespace: default
spec:
  secretName: backend-tls-secret
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
  - backend.myCompany.com
  # 自動在到期前 30 天更新
  renewBefore: 720h

最佳實踐: 對於需要自動更新的 Let’s Encrypt 證書,使用 cert-manager。對於需要 OV/EV 等級的企業證書,使用 TWCA 並設定到期提醒 (可以建立一個 Kubernetes CronJob - 自動化且不需要額外的監控基礎設施,定期檢查憑證並在即將到期時發送通知)。

驗證與測試

證書檢查

驗證證書已正確部署:

# 檢查證書頒發者和有效期
echo | openssl s_client -servername backend.myCompany.com \
  -connect backend.myCompany.com:443 2>/dev/null | \
  openssl x509 -noout -text | \
  grep -E "(Issuer:|Subject:|Not After|DNS:)"

預期輸出:

Issuer: C=TW, O=TAIWAN-CA, CN=TWCA Secure SSL Certification Authority
Subject: C=TW, ST=Taiwan, L=Taipei, O=ITCompany Inc., CN=backend.myCompany.com
Not After : Apr 28 15:59:59 2026 GMT
DNS:backend.myCompany.com

HTTPS 功能測試

測試 HTTP 到 HTTPS 的自動重定向:

# 測試重定向
curl -I http://backend.myCompany.com

# 預期輸出
HTTP/1.1 308 Permanent Redirect
Location: https://backend.myCompany.com

測試 HSTS 標頭:

# 檢查 HSTS 標頭
curl -I https://backend.myCompany.com | grep -i strict

# 預期輸出
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

完整的安全性檢查清單:

檢查項目驗證方法預期結果
✓ TLS 版本curl -I https://...TLS 1.2 或 1.3
✓ 證書鏈openssl s_client -showcerts完整鏈(3 層)
✓ HSTS 標頭curl -I https://...max-age=31536000
✓ HTTP 重定向curl -I http://...308 Permanent Redirect
✓ 混合內容瀏覽器開發者工具無警告

清理 DNS 驗證記錄

證書部署完成後,ACME 驗證用的 DNS TXT 記錄可以安全刪除:

# 刪除 ACME challenge 記錄
aws route53 change-resource-record-sets \
  --hosted-zone-id Z01234567XOQOJYWWWUAC \
  --change-batch file://delete-acme-records.json

注意: 不要刪除其他重要的 DNS 記錄(如 _dmarc 用於郵件驗證)。

驗證刪除成功:

# 確認記錄已刪除
aws route53 list-resource-record-sets \
  --hosted-zone-id Z01234567XOQOJYWWWUAC \
  --query "ResourceRecordSets[?Type=='TXT' && contains(Name, '_acme-challenge')]"

# 預期輸出
[]

最佳實踐與注意事項

證書更新管理

TWCA OV 證書有效期約 12 個月,需要定期更新:

更新流程:

  1. 提前 30 天續約:避免證書過期導致服務中斷
  2. 設定監控告警
    # 檢查證書到期時間
    echo | openssl s_client -servername backend.myCompany.com \
      -connect backend.myCompany.com:443 2>/dev/null | \
      openssl x509 -noout -enddate
    
  3. 自動化流程:建立 CI/CD pipeline 自動更新證書

推薦工具:

  • Prometheus + Alertmanager:監控證書到期時間
  • cert-manager:自動更新 Let’s Encrypt 證書
  • AWS Lambda:定期檢查並發送提醒

常見問題處理

問題 1:證書鏈不完整

  • 症狀:部分瀏覽器顯示「證書不受信任」
  • 原因:未包含中繼憑證(ca-bundle.crt)
  • 解決cat certificate.crt ca-bundle.crt > fullchain.crt

問題 2:私鑰不匹配

  • 症狀:Ingress 無法啟動,日誌顯示 “key values mismatch”
  • 原因:私鑰與 CSR 不對應
  • 檢查
    # 檢查證書和私鑰是否匹配
    openssl x509 -noout -modulus -in certificate.crt | openssl md5
    openssl rsa -noout -modulus -in private.key | openssl md5
    # 兩個 MD5 值必須相同
    

問題 3:DNS 驗證失敗

  • 症狀:TWCA 無法驗證網域所有權

  • 原因:DNS 記錄未生效或 TTL 過長

  • 解決

    方法 1:使用命令列工具

    # 使用 dig 確認記錄已生效
    dig _acme-challenge.backend.myCompany.com TXT +short
    
    # 預期輸出(範例)
    "Kg7PqRsT9uVwXyZaBCdEfGhIjKlMnOpQ2rS3tU4vW"
    

    方法 2:使用 Google Admin Toolbox(推薦)

    如果你沒有安裝 dig 命令,可以使用 Google 提供的線上工具:

    1. 開啟 Google Admin Toolbox - Dig
    2. Name 欄位填入:abc.com
    3. Type 下拉選單選擇:TXT
    4. 點選 Dig 按鈕

    預期看到的結果:

    ;; ANSWER SECTION:
    abc.com. 300 IN TXT "Kg7PqRsT9uVwXyZaBCdEfGhIjKlMnOpQ2rS3tU4vW"
    

    如果看到上述記錄,表示 DNS 已正確設定且已傳播生效。

    方法 3:檢查全球 DNS 傳播狀態

    使用 whatsmydns.net 檢查全球不同地區的 DNS 伺服器是否都能查到記錄:

    • 輸入網域:myCompany.com
    • 選擇記錄類型:TXT
    • 查看世界各地的 DNS 伺服器回應(通常需要 5-10 分鐘全球傳播)

安全性建議

  1. 私鑰保護

    # 設定 RBAC 限制 Secret 存取
    kubectl create role secret-reader \
      --verb=get --resource=secrets
    
    kubectl create rolebinding secret-reader-binding \
      --role=secret-reader \
      --serviceaccount=default:ingress-controller
    
  2. 定期更換:即使未到期,建議每 3 個月更換私鑰

  3. 監控異常

    # 檢查 Ingress 日誌,偵測 TLS 握手失敗
    kubectl logs -n ingress-nginx deployment/ingress-nginx-controller | \
      grep "SSL_do_handshake"
    
  4. 啟用證書透明度監控:使用 crt.sh 監控你的網域是否有未授權的證書簽發

總結

在 Kubernetes 環境部署 TWCA OV SSL 證書需要完整理解:

核心概念:

  • SSL 分級:DV/OV/EV 的差異與適用場景
  • HTTPS 原理:TLS 握手、證書鏈驗證、金鑰交換
  • TLS Secret:Kubernetes 中儲存證書的機制
  • HSTS:強制 HTTPS 的安全標頭

關鍵步驟:

  1. DNS 驗證:透過 Route53 建立 ACME challenge TXT 記錄
  2. TLS Secret:正確包含證書鏈和私鑰
  3. Ingress 配置:啟用 SSL 強制重定向和 HSTS
  4. 自動化工具:使用 cert-manager 自動更新證書

AWS 整合:

  • Route53:DNS 管理和 ACME 驗證
  • ACM:適合 AWS Load Balancer 的免費證書
  • cert-manager:Kubernetes 原生的證書自動化

透過正確的配置,TWCA OV 證書能為你的 Kubernetes 應用提供企業級的 SSL/TLS 加密保護,同時滿足台灣在地的合規要求。