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

§1

一句話結論

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

§2

這一棒檢查什麼

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 行)。

§3

總覽表

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

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

  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 指紋,無法從指紋反推出密碼。
§5

可信度(分兩層看)

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

執行概況(照 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/