- 將 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>
331 lines
11 KiB
Markdown
331 lines
11 KiB
Markdown
# 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 首次實際執行時踩到的坑,記錄下來避免日後重蹈覆轍。
|
||
|
||
### 坑 1:TWCA cert.zip 裡的副檔名是 `.cer`,不是 `.crt`
|
||
|
||
TWCA 核發的壓縮檔解開是 `root.cer`、`uca.cer`、`server.cer`,都是 PEM 格式但副檔名為 `.cer`。指令中的 `*.crt` 要改成 `*.cer`,否則找不到檔案。
|
||
|
||
### 坑 2:Private 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` 原始檔。
|
||
|
||
### 坑 3:Docker 指令需要 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/` 取得。
|
||
|
||
### 坑 5:OCSP 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.pem,Caddy 只需 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 timeout(log 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 憑證
|
||
└── ...
|
||
```
|