Files
106_105_181_40/docs/sop-update-caddy-ssl.md
Timmy 97fd841a0c 新增分拆文件、SOP 與 Caddy SSL 憑證更新紀錄
- 將 hospital_media_antmedia.md 分拆為 docs/ 下五份主題文件
  (host.md, caddy.md, antmedia.md, ssl.md, maintenance.md)
- 新增 README.md / QUICKSTART.md / SUMMARY.md 作為文件入口
- 新增 SOP:Caddy SSL 憑證更新流程 (docs/sop-update-caddy-ssl.md)
  含 2026-04-16 實戰踩坑紀錄(.cer 副檔名、私鑰不匹配、sudo、防火牆 TLS 終端)
- 更新各文件中 tel.tpech.gov.tw 憑證資訊:HiPKI → TWCA,到期 2026-10-30
- 新增 .gitignore 排除 ssl-staging/(含私鑰,不可進 git)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 10:54:47 +08:00

11 KiB
Raw Permalink Blame History

SOP更新 Caddy SSL 憑證(tel.tpech.gov.tw

本 SOP 用於把新申請到的 SSL 憑證部署到 /opt/caddy/,讓 tel.tpech.gov.tw 使用新憑證。

本 SOP 只處理 Caddy 的憑證(tel.tpech.gov.tw)。 Ant Media Server 的 tel-server.tpech.gov.tw另一張獨立憑證,更新方式見 ssl.md


實戰紀錄2026-04-16 首次執行)

以下是本 SOP 首次實際執行時踩到的坑,記錄下來避免日後重蹈覆轍。

坑 1TWCA cert.zip 裡的副檔名是 .cer,不是 .crt

TWCA 核發的壓縮檔解開是 root.ceruca.cerserver.cer,都是 PEM 格式但副檔名為 .cer。指令中的 *.crt 要改成 *.cer,否則找不到檔案。

坑 2Private Key 不在主機上

主機 /opt/caddy/privkey.pem 裡面是舊的 HiPKI 那把 key。這次向 TWCA 申請時用的是新產生的 CSR / key pair,所以舊 key 和新憑證的 modulus 不匹配。

最後在本機 ~/Downloads/ssl/tel/tel.key2026-03-26 產生)找到正確的 key。

教訓申請憑證時key 和 CSR 一定要放到固定位置並標註日期與用途。建議統一放到 ssl-staging/<網域>-<年月>/ 並保留 .key + .csr 原始檔。

坑 3Docker 指令需要 sudo

tpech_1duan 使用者不在 docker group 裡,./restart.sh 必須加 sudo

坑 4醫院前端有防火牆 / WAF 做 TLS 終端

Caddy 重啟後,主機本地 127.0.0.1:443 驗證是新的 TWCA 憑證,但從外部106.105.181.40:443 看到的還是舊的 HiPKI 憑證。

原因:醫院網路在伺服器前面有一層設備(防火牆 / WAF / load balancer在做 SSL 終端或 SSL inspection。外部 TLS 連線是跟那台設備交握,不是直接跟 Caddy 交握。

需要額外通知網管單位:把新憑證也更新到前端防火牆/WAF 上。憑證檔案可以從主機 /opt/caddy/hospital/ssl_tel_tpech_gov_tw_2026_10/ 取得。

坑 5OCSP Stapling timeout

Caddy log 中出現 TWCA 的 OCSP 伺服器 twcasslocsp.twca.com.tw 連線 timeout。原因可能是主機對外存取被限制。這只是 warning不影響 TLS 正常運作,但客戶端可能需要自行查 OCSP。


憑證資訊

目前生效的憑證2026-04-16 部署)

項目
網域 tel.tpech.gov.tw
簽發者 TWCA Secure SSL Certification Authority
憑證序號 95590185165637134231279949412783532535
生效日 2026-04-15
到期日 2026-10-30(僅半年)
憑證檔 cert.zip(內含 root.ceruca.cerserver.cer
私鑰來源 本機 ~/Downloads/ssl/tel/tel.key2026-03-26 產生)
主機備份 /opt/caddy/hospital/ssl_tel_tpech_gov_tw_2026_10/

被替換的舊憑證

項目
簽發者 中華電信 HiPKI OV TLS CA - G1
有效期 2025-04-23 ~ 2026-04-23
主機備份 /opt/caddy/hospital/ssl_tel_tpech_gov_tw_pre_2026_10_backup/

注意:這張憑證只有半年效期,2026-10 月前必須再次更新。


0. 前置檢查(動手前必確認)

# 檢查項 指令 / 說明
A cert.zip TWCA 寄來的附件。解壓後應有 3 個 .cer
B Private Key 當初產生 CSR 時留下的私鑰。TWCA 不會寄這個,必須自己保留
C SSH 可連 ssh hospital_media "echo ok"
D Caddy 活著 ssh hospital_media "sudo docker ps | grep caddy"

找不到私鑰怎麼辦?

  1. 先搜本機:find ~/Downloads ~/Desktop ~/Documents -name '*.key' -newer <cert.zip 的日期>
  2. 再搜主機:ssh hospital_media "find /opt /root /home -maxdepth 4 -name '*.key' 2>/dev/null"
  3. 都找不到 → 重新產生 CSR/key向 TWCA 申請 rekey重核發

1. 本機準備工作目錄

工作區放在 repo 下的 ssl-staging/(已加入 .gitignore,不會 commit

cd /Users/timmy/Projects/Freelance/106_105_181_40
mkdir -p ssl-staging/tel-tpech-<年月>      # 例tel-tpech-2026-10
cd ssl-staging/tel-tpech-<年月>

cert.zipprivkey.pem(或 *.key)都放進來。


2. 解壓

unzip cert.zip
ls
# TWCA 格式root.cer  uca.cer  server.cer
# (注意副檔名是 .cer不是 .crt

3. 檢查憑證格式與內容

# 確認是 PEM 文字格式
file *.cer
# 應為 "PEM certificate" 或 "ASCII text"

# 確認網域、簽發者、效期
openssl x509 -in server.cer -noout -subject -issuer -dates

若是 DER 格式(file 顯示 "data" 或 "ASN.1"),轉 PEM

for f in root.cer uca.cer server.cer; do
  openssl x509 -inform DER -in "$f" -out "$f.pem" && mv "$f.pem" "$f"
done

4. 合成 fullchain.pem 並命名私鑰

# 合併server 在前,中繼 CA 在後(順序不可反)
cat server.cer uca.cer > fullchain.pem

# 私鑰統一命名
cp <你的key檔> privkey.pem    # 例cp tel.key privkey.pem
ls privkey.pem                 # 確認存在

root.cer 不需放進 fullchain.pemCaddy 只需 leaf + intermediate 就夠。


5. 驗證「憑證」與「私鑰」是否匹配

最重要的一步。錯了貼上去 Caddy 會起不來。

openssl x509 -in fullchain.pem -noout -modulus | openssl md5
openssl rsa  -in privkey.pem   -noout -modulus | openssl md5

兩個 MD5 必須完全相同

# 正確範例(兩邊一樣):
# MD5(stdin)= 1c4ae9b6fae3920bb30f76ac792db989
# MD5(stdin)= 1c4ae9b6fae3920bb30f76ac792db989

若不同 → 立刻停。代表這把 key 不是產生 CSR 的那把。回頭找對的 key參考步驟 0 的搜尋方法。

2026-04-16 實例:主機上的 /opt/caddy/privkey.pem(舊 HiPKI 那把modulus 為 c4b56aed...,新憑證為 1c4ae9b6...,不匹配。最後在本機 ~/Downloads/ssl/tel/tel.key 找到匹配的。


6. 備份主機上的舊憑證

ssh hospital_media "sudo mkdir -p /opt/caddy/hospital/ssl_tel_tpech_gov_tw_pre_<年月>_backup && \
  sudo cp /opt/caddy/fullchain.pem /opt/caddy/privkey.pem \
          /opt/caddy/hospital/ssl_tel_tpech_gov_tw_pre_<年月>_backup/ && \
  ls -l /opt/caddy/hospital/ssl_tel_tpech_gov_tw_pre_<年月>_backup/"

7. 上傳新憑證到主機

# 經由 /tmp 中轉,避免權限問題
scp fullchain.pem privkey.pem hospital_media:/tmp/
ssh hospital_media "sudo mv /tmp/fullchain.pem /tmp/privkey.pem /opt/caddy/ && \
  sudo chmod 644 /opt/caddy/fullchain.pem && \
  sudo chmod 600 /opt/caddy/privkey.pem && \
  ls -l /opt/caddy/fullchain.pem /opt/caddy/privkey.pem"

8. 重啟 Caddy

# 注意:需要 sudo否則 permission denied
ssh hospital_media "cd /opt/caddy && sudo ./restart.sh"

確認 log 無報錯:

ssh hospital_media "sudo docker logs caddy --tail 20"
# 看到 "serving initial configuration" 且無 error 就算成功
# OCSP stapling timeout 是 warning可忽略

9. 驗證

9a. 主機本地驗證(必做,最可靠)

ssh hospital_media "openssl s_client -connect 127.0.0.1:443 \
  -servername tel.tpech.gov.tw </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates"

# 預期:
# subject= ... CN=tel.tpech.gov.tw
# issuer= ... TWCA ...
# notAfter= ...

看到新的 issuer 和到期日 → Caddy 端完成。

9b. 外部驗證(可能看到舊憑證!)

openssl s_client -connect tel.tpech.gov.tw:443 \
  -servername tel.tpech.gov.tw </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -dates

已知問題:醫院前端有防火牆 / WAF 做 TLS 終端,從外面連進去看到的可能還是舊憑證。 這不是 Caddy 的問題——需要另外通知網管單位更新防火牆/WAF 上的憑證。 見下方「防火牆 / WAF 注意事項」。


10. 歸檔到主機備份目錄

scp cert.zip fullchain.pem privkey.pem server.cer uca.cer root.cer \
    hospital_media:/tmp/

ssh hospital_media "sudo mkdir -p /opt/caddy/hospital/ssl_tel_tpech_gov_tw_<年月> && \
  sudo mv /tmp/cert.zip /tmp/fullchain.pem /tmp/privkey.pem \
          /tmp/server.cer /tmp/uca.cer /tmp/root.cer \
          /opt/caddy/hospital/ssl_tel_tpech_gov_tw_<年月>/ && \
  ls -l /opt/caddy/hospital/ssl_tel_tpech_gov_tw_<年月>/"

11. 通知網管 + 更新文件

通知網管更新防火牆/WAF

發信或工單給網管單位,內容:

tel.tpech.gov.tw:443 的 SSL 憑證已在伺服器端更新為 TWCA 新憑證(到期 YYYY-MM-DD請同步更新防火牆 / 負載均衡器上的 TLS 終端憑證。 新憑證檔案位置:主機 /opt/caddy/hospital/ssl_tel_tpech_gov_tw_<年月>/

更新本 repo 文件

把以下文件裡 tel.tpech.gov.tw 的 CA 與到期日更新:


防火牆 / WAF 注意事項

106.105.181.40:443 前面有醫院的網路設備做 TLS 終端:

外部用戶 → [防火牆/WAF :443 (TLS 終端)] → [106.105.181.40 Caddy :443]
  • Caddy 裝了新憑證,只能讓「防火牆 → Caddy」這段用新憑證。
  • 「外部用戶 → 防火牆」那段用的是防火牆上安裝的憑證,需要網管另外處理。
  • 驗證是否全程更新完成:從外部執行 openssl s_client,看到新的 issuer 才算全部搞定。

回滾流程(萬一 Caddy 起不來)

ssh hospital_media "sudo cp /opt/caddy/hospital/ssl_tel_tpech_gov_tw_pre_<年月>_backup/fullchain.pem \
  /opt/caddy/hospital/ssl_tel_tpech_gov_tw_pre_<年月>_backup/privkey.pem \
  /opt/caddy/ && \
  cd /opt/caddy && sudo ./restart.sh"

回滾後檢查第 5 步的 MD5 是否真的一致、第 4 步的 cat 順序是否正確。


常見地雷

症狀 通常原因 參考步驟
Caddy 起來但 ERR_SSL_PROTOCOL_ERROR 憑證/私鑰不匹配 第 5 步
NET::ERR_CERT_AUTHORITY_INVALID fullchain.pem 少了中繼 CA 第 4 步
tls: failed to parse private key 私鑰格式不對(加密了 / DER 沒轉 PEM 第 3 步
scp 上去後 Caddy 讀不到 權限不對644/600 第 7 步
./restart.sh permission denied 忘了加 sudo 第 8 步
外部仍看到舊憑證、本地是新的 醫院防火牆/WAF TLS 終端未更新 第 9b 步
OCSP stapling timeoutlog warning 主機對外連線受限,不影響功能 可忽略
主機上的 privkey.pem 和新憑證不匹配 申請新憑證時用了新 CSR/key舊 key 當然不配 第 0/5 步

主機備份目錄結構(供日後查閱)

/opt/caddy/hospital/
├── ssl_tel_tpech_gov_tw_2025/                    # 2025 HiPKI 憑證原件
├── ssl_tel_tpech_gov_tw_pre_2026_10_backup/      # 2026-04 更新前的舊檔備份
├── ssl_tel_tpech_gov_tw_2026_10/                  # 2026 TWCA 憑證原件
├── ssl_tel_server_tpech_gov_tw_2025/             # tel-server 2025 憑證
└── ...