# FR-187 F1 掃描報告：設定檔加密的主系統專屬部分（維運指令、舊機轉換、初始密碼、改網址）

## 一句話結論

**工具沒有找到問題（0 條）。** 「重點看什麼」7 條我逐條開檔看過，沒有一條能讓攻擊者拿到原本拿不到的東西；有一個不急的小缺口：`config edit` 編輯到一半被強制中斷時，明碼副本會留在記憶體目錄裡，重開機才消失。注意：工具這次是「研究員一條都沒提，所以三個檢查員沒有東西可投」，不是「三個檢查員投票確認乾淨」。

## 這一棒檢查什麼

FR-179 把兩份設定檔（`.env`、`guidant.env`）改成加密存在磁碟，解密的鑰匙交給主機的安全晶片（TPM）密封保管，明碼只放在記憶體目錄 `/run/guidant`。這一棒看的是主系統獨有的周邊程式：

- `guidantai config edit`、`rotate-key`（換鑰匙）、`psql`、`redis-cli` 這些維運指令（`scripts/installer/lib/guidantai_secrets_cmds.sh`）；
- 舊機從明碼轉成加密、三把舊資料加密鑰併成一把（`scripts/installer/lib/upgrade_convert_secrets.sh`）；
- 開機時解密到記憶體的 systemd 服務（`scripts/installer/systemd/guidant-secrets.service`，systemd 是 Linux 管開機服務的程式）；
- 系統帳號 admin 的初始密碼（`scripts/installer/lib/root_admin_password.sh`）；
- 改系統網址時跟著改憑證與設定（`scripts/installer/lib/site_url.sh`）；
- 資料目錄擁有者（`data_dir_owner.sh`）與設定檔指紋（`config_digest.sh`）。

共 7 檔、約 578 行。核心 `secrets.sh`／`tpm.sh` 不在本棒（由 FR-186 H3 掃）；只為查證讀了 `secrets.sh` 的 `sec_key_migrate_legacy`（第 83–96 行）與 `sec_ready_run_dir`（第 203 行），以及 `upgrade_common.sh` 的 `upg_set_kv`（第 100–108 行）。

## 總覽表

| # | 這是什麼 | 出事會怎樣 | 要先有什麼才打得到 | 在哪裡 | 怎麼修 | 嚴重度 |
|---|---|---|---|---|---|---|
| （工具） | 無 | — | — | — | — | — |
| 人工備註 | `config edit` 中途被強制中斷（Ctrl+C、斷線）時，明碼編輯副本不會被刪 | 明碼留在 `/run/guidant/.edit.XXXXXX`，直到重開機；不落磁碟、只有 root 讀得到 | 本來就是 root | `guidantai_secrets_cmds.sh:42-44` | 建副本後掛 `trap 'rm -f -- "$tmp"' EXIT INT TERM HUP` | 很低（不建議單獨開卡，可併入下次動這支的卡） |

## 「重點看什麼」逐條人工查證

1. **`config edit` 的 `$EDITOR` 與明碼副本（`guidantai_secrets_cmds.sh:32-63`）— 不成立（附一個很低的備註）。**
   - `sudo` 預設會清掉環境變數（env_reset），EDITOR 不會被帶過去，除非管理員自己設 `sudo -E` 或 env_keep。就算帶過去了，能跑 `sudo` 的人本來就是 root，指定編輯器不會多拿到任何權限。
   - 副本用 `umask 077` 建在 `/run/guidant`（記憶體，`sec_ready_run_dir` 設成 700），不落磁碟，這是對的。
   - `_config_edit_apply` 不管成功還是失敗都會回到第 44 行的 `rm -f -- "$tmp"`，正常失敗的路徑會清掉副本。**唯一缺口**是在編輯器開著的時候行程被打斷（SIGINT／SIGHUP）：沒有掛 trap，副本會一直留在記憶體目錄，直到重開機（`ExecStop` 也會在停服務時清掉 `/run/guidant`）。這不違反「磁碟上沒有明碼」，影響很低。
   - 另外，第 59 行先寫密文、第 60 行再換記憶體檔；第 60 行失敗時磁碟是新設定、記憶體是舊設定，下次開機就會套上新設定。這是一致性問題，不是資安問題。
2. **其他子指令會不會印出明碼（`guidantai_secrets_cmds.sh:12-21, 75-122`）— 不成立。** `status` 只印「已解開／未解開」與原因代碼；`rotate-key` 只印成功或失敗；`psql`、`redis-cli` 的密碼放在環境變數，用 `-e PGPASSWORD`／`-e REDISCLI_AUTH`（只帶名字不帶值）交給容器，不上命令列（`ps` 看不到），也不印到畫面。
3. **舊機轉換有沒有留下明碼（`upgrade_convert_secrets.sh`、`secrets.sh:83-96`）— 不成立。** 轉換成功後，`ucs_shred_upgrade_snapshot`（第 161–165 行）會銷毀升級前快照裡的兩份明碼；已經轉過的機器再升級時，第 135 行會銷毀上次中斷留下的磁碟明碼；`ucs_drop_keys` 的暫存檔 `.ucstmp` 會被 `mv` 換上，失敗時也會刪掉。`sec_key_migrate_legacy` 比對用的 `.chk` 放在記憶體目錄、用完即刪，舊的磁碟鑰匙在密封成功後才 `sec_shred`。三把舊鑰匙交給 init 容器時用 `-e 名稱`（不帶值），不上命令列。另外，改網址重產憑證時，舊的 `server.key` 會備份到 `.upgrade-config-prev/server-cert-before-reissue`（700）：那是前門憑證的私鑰，不是設定檔密碼，屬於設計內的備份。
4. **`guidant-secrets.service` 的身分、權限、順序 — 不成立。** 沒有寫 `User=`，所以以 root 跑；`/run/guidant` 由 `sec_ready_run_dir` 建、權限 700。它是 `Type=oneshot` 加上 `Before=docker.service`，docker 會等它跑完才起，容器不會在解密完成前讀到檔案。`sec_load_all` 的設計是「全部解成功才換上」，不會有讀到半套檔的時機。解不開時 docker 照起、容器讀不到設定檔而反覆重啟，這是刻意選擇（檔頭有寫理由）。
5. **初始管理員密碼（`root_admin_password.sh`）— 不成立。** 用 `read -s` 輸入（不回顯，所以不會進安裝紀錄檔），只存在 shell 變數，不寫檔、不印出；錯誤訊息只講規則，不帶值。非互動安裝從客戶自己的設定檔讀，那份檔由客戶保管（安裝後清掉屬於手冊範圍）。
6. **`site_url.sh` 會不會被換行注入 — 不成立。** SYSTEM_URL 是用 `read_env_kv` 從 `guidant.env` 讀出的一行，本身不可能含換行；寫回時 `upg_set_kv` 用 awk 從環境變數取值，不經 shell 展開。能改 SYSTEM_URL 的人就是能改 `guidant.env` 的 root，本來就能直接改任何設定，沒有權限落差。（主機名會被放進 openssl 的 `-subj`，值裡如果有 `/` 會多出憑證欄位，但來源同樣是 root 自己，只影響自簽憑證的內容。）
7. **`chown -R` 跟符號連結、`config_digest.sh` 的用途 — 不成立。** GNU `chown -R` 預設不跟著符號連結走（`-P`），只改連結本身；目錄路徑來自 root 擁有的 `.env` 或安裝流程。`config_digest` 只給 `guidantai restart` 判斷「設定改了還沒套用」，不是安全判斷；寫到資料目錄的是 sha256 指紋，無法從指紋反推出密碼。

## 可信度（分兩層看）

- **「列出的問題存在嗎？」**：工具列出 0 條，沒有可核對的東西；人工備註那一條我開檔確認過（第 42–44 行之間沒有 trap）。
- **「只有這些嗎？」**：low 檔次是一位研究員讀完整個範圍、檢查所有類型的漏洞，研究員有回報（1 派 1 回）。但研究員沒有提出任何候選，三人投票就沒有東西可投，所以「乾淨」只有一位研究員加上我人工逐條看過做背書，沒有經過投票。工具不會執行程式，也沒有做任何實際攻擊驗證。

## 執行概況（照 stamp 抄）

- run ID：`wf_8804b70b-ee5`；報告目錄 `CLAUDE-SECURITY-20261108-191400`；掃描的版本 `1c9be8d84`，工作目錄是乾淨的
- `verification.status`：**verified**；`reason_kind`：null
- `candidates`：0；`panel_votes`：0（**candidates 為 0 時 status 也會是 verified，意思是「沒東西可投」，不是「投票確認乾淨」**）
- `researchers_dispatched`：1；`researchers_returned`：1
- 研究員花了約 14 分鐘、約 12 萬 token；沒有失敗或重試
- 歸檔：`docs/features/security-scan-consolidated/runs/be/CLAUDE-SECURITY-20261108-191400/`
