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

331 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](ssl.md)。
---
## 實戰紀錄2026-04-16 首次執行)
以下是本 SOP 首次實際執行時踩到的坑,記錄下來避免日後重蹈覆轍。
### 坑 1TWCA cert.zip 裡的副檔名是 `.cer`,不是 `.crt`
TWCA 核發的壓縮檔解開是 `root.cer``uca.cer``server.cer`,都是 PEM 格式但副檔名為 `.cer`。指令中的 `*.crt` 要改成 `*.cer`,否則找不到檔案。
### 坑 2Private Key 不在主機上
主機 `/opt/caddy/privkey.pem` 裡面是舊的 HiPKI 那把 key。這次向 TWCA 申請時用的是**新產生的 CSR / key pair**,所以舊 key 和新憑證的 modulus 不匹配。
最後在本機 `~/Downloads/ssl/tel/tel.key`2026-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.cer``uca.cer``server.cer` |
| 私鑰來源 | 本機 `~/Downloads/ssl/tel/tel.key`2026-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
```bash
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.zip``privkey.pem`(或 `*.key`)都放進來。
---
## 2. 解壓
```bash
unzip cert.zip
ls
# TWCA 格式root.cer uca.cer server.cer
# (注意副檔名是 .cer不是 .crt
```
---
## 3. 檢查憑證格式與內容
```bash
# 確認是 PEM 文字格式
file *.cer
# 應為 "PEM certificate" 或 "ASCII text"
# 確認網域、簽發者、效期
openssl x509 -in server.cer -noout -subject -issuer -dates
```
### 若是 DER 格式(`file` 顯示 "data" 或 "ASN.1"),轉 PEM
```bash
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 並命名私鑰
```bash
# 合併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 會起不來。**
```bash
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. 備份主機上的舊憑證
```bash
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. 上傳新憑證到主機
```bash
# 經由 /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
```bash
# 注意:需要 sudo否則 permission denied
ssh hospital_media "cd /opt/caddy && sudo ./restart.sh"
```
確認 log 無報錯:
```bash
ssh hospital_media "sudo docker logs caddy --tail 20"
# 看到 "serving initial configuration" 且無 error 就算成功
# OCSP stapling timeout 是 warning可忽略
```
---
## 9. 驗證
### 9a. 主機本地驗證(必做,最可靠)
```bash
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. 外部驗證(可能看到舊憑證!)
```bash
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 注意事項](#防火牆--waf-注意事項)」。
---
## 10. 歸檔到主機備份目錄
```bash
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 與到期日更新:
- [docs/caddy.md](caddy.md) — `Caddy SSL 憑證`
- [docs/ssl.md](ssl.md) — `憑證總覽`
- [docs/maintenance.md](maintenance.md) — `SSL 憑證到期時間`
- [SUMMARY.md](../SUMMARY.md) — 第 6 節
---
## 防火牆 / WAF 注意事項
`106.105.181.40``:443` 前面有醫院的網路設備做 TLS 終端:
```
外部用戶 → [防火牆/WAF :443 (TLS 終端)] → [106.105.181.40 Caddy :443]
```
- Caddy 裝了新憑證,只能讓「防火牆 → Caddy」這段用新憑證。
- 「外部用戶 → 防火牆」那段用的是**防火牆上安裝的憑證**,需要網管另外處理。
- 驗證是否全程更新完成:從外部執行 `openssl s_client`,看到新的 issuer 才算全部搞定。
---
## 回滾流程(萬一 Caddy 起不來)
```bash
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 憑證
└── ...
```