題幹與適用場景
公司每天從 GitHub Actions 與自託管 Runner 建置數千個容器映像。請設計服務,為每個映像產生並驗證可追溯的建置 Provenance,只有符合組織策略的制品才能部署。說明資料模型、簽發與驗證、信任根、金鑰輪換、Runner 遭入侵、離線部署、回滾與可觀測性。
公開的供應鏈安全招聘設計題已把「為數千個服務與混合 Runner 產生可驗證 Provenance」作為系統設計提示。SLSA 將 Provenance 的產生、分發、驗證與建置平台評估分開;Sigstore 文件則要求驗證簽名、身分、發行者與制品摘要。題目核心是建立可稽核信任鏈,不是替映像倉庫加一個簽名欄位。
面試官考察重點
- 區分制品摘要、Provenance 聲明、簽名、透明日誌與部署策略。
- 把不可信的原始碼、依賴、建置設定與 Runner 納入威脅模型。
- 讓證明綁定不可變制品,而非可變標籤或建置結果之外的文字。
- 定義驗證失敗、金鑰輪換、撤銷、離線與回滾語意。
- 估算寫入、查詢、快取、保存與稽核成本,設置策略版本和觀測指標。
回答前需要釐清的問題
- 目標是阻止未授權制品部署,還是只提供稽核?預設部署准入是強門檻,稽核證據獨立保存。
- 制品類型只有容器嗎?先支援 OCI 映像,抽象介面可延伸到二進位與套件。
- 建置環境都能連網嗎?預設存在離線或受限網路,需要預快取信任根與證明。
- 簽名身分按人、倉庫、工作流還是建置平台?預設用工作流身分和受管建置器。
- 需要保存多久?先取得合規與稽核期限,再推導物件儲存、索引與日誌保存。
30 秒回答框架
我會把流程拆成四個邊界:建置器產生包含原始版本、依賴、建置參數與建置器身分的 Provenance;簽發服務把聲明綁定到不可變制品摘要並寫入透明日誌;驗證器在部署前檢查簽名鏈、身分、摘要、策略版本和時間窗口;准入控制器決定允許、隔離或拒絕。原始碼、依賴和 Runner 都是不可信輸入,不能把 CI 成功當成證明。策略版本、失敗原因和回滾版本要可追蹤,離線環境使用固定信任根和證明快取。
分步驟深入解答
第一步:建立信任邊界與威脅模型
列出原始碼倉庫、依賴解析器、建置設定、Runner、制品倉庫、簽發服務和部署控制器。攻擊者可能篡改依賴、竊取 Runner 憑證、替換映像標籤、偽造聲明或重播舊證明。要明確證明「哪個摘要由哪個受信建置器按哪些輸入產生」,不要聲稱證明程式碼本身沒有漏洞。
第二步:定義 Provenance 與制品綁定
證明至少包含原始提交摘要、建置器身分、建置入口、依賴鎖定資訊、參數、時間、步驟摘要與輸出制品摘要。制品摘要是綁定鍵;標籤只用於尋找,不能用於授權。證明、簽名和驗證結果都記錄 schema 版本,避免欄位語意靜默改變。
artifact_digest -> provenance_digest -> signature -> log_entry
policy_version + identity + builder + time_window -> admission_decision第三步:設計簽發與透明日誌
建置器提交聲明,簽發服務驗證建置身分和格式後簽名或產生可驗證簽名包。簽名物件必須涵蓋制品摘要和關鍵聲明;透明日誌用來發現同一身分的異常簽發,不取代部署時策略判斷。高吞吐寫入可先落不可變儲存,再異步建立依摘要、倉庫和身分索引。
第四步:實作部署前驗證
驗證器先解析不可變摘要,再檢查簽名鏈、信任根、憑證身分、發行者、聲明完整性和日誌證明,最後套用組織策略。例如只允許主分支、受管 Runner、通過依賴掃描且未過期的聲明。結果包含 allow、quarantine 或 deny、策略版本與原因碼,不能只回傳布林值。
第五步:處理金鑰、身分與 Runner 風險
優先使用短期工作流身分或金鑰託管服務,限制簽發權限與受眾。金鑰輪換不應令歷史制品全部失效;驗證器要保留舊信任根的有效期和撤銷狀態。Runner 遭入侵時撤銷身分、凍結受影響工作流、標記相關證明並阻止新制品准入;已部署制品依風險重新驗證或回滾。
第六步:覆蓋重播、回滾與離線驗證
證明帶建置時間、版本與策略適用窗口,驗證器拒絕超過窗口或與摘要不符的聲明。回滾仍需驗證舊摘要符合目前或明確相容的策略,不能因曾部署過就直接放行。離線環境預置信任根、撤銷快照與證明包,記錄快照年齡;超過上限就隔離。
第七步:估算容量、保存與故障降級
按每日制品數、平均證明大小、簽名寫入、驗證 QPS 和部署峰值估算儲存與索引。證明本體放便宜不可變儲存,熱索引只保留近期摘要。簽發不可用時停止新制品准入或隔離;驗證器不可用時,生產環境 fail-closed,低風險環境可使用有期限快取並記錄例外。
第八步:設計可觀測性與遷移
記錄每次驗證的制品摘要、策略版本、信任根版本、原因碼與耗時,不記錄不必要的原始碼或金鑰。監控證明覆蓋率、簽名失敗、身分異常、策略拒絕、快取命中、日誌延遲與 Runner 撤銷數。策略升級先影子評估再按環境灰度,每次拒絕都能重播輸入並解釋決策。
高品質示範回答
我會把系統分成建置證明、簽發與透明日誌、部署驗證、准入策略四層。建置器產生綁定提交、依賴、參數、建置器身分與輸出摘要的 Provenance;簽發服務驗證工作流身分後,把簽名綁定不可變摘要並寫入日誌。部署前驗證器檢查簽名鏈、憑證身分、發行者、聲明完整性、信任根、時間窗口和策略版本,再回傳帶原因碼的允許、隔離或拒絕。標籤不是授權依據。短期身分、受管 Runner、金鑰輪換與撤銷處理憑證風險;離線環境使用有年齡上限的信任根、撤銷快照和證明快取。驗證失敗或服務故障依環境採用 fail-closed、隔離或受限快取,完整記錄策略版本與決策證據。
常見錯誤
- 只簽署映像標籤,標籤移動後仍誤以為制品可信。
- 只驗證簽名數學正確,不驗證憑證身分、發行者和聲明內容。
- 把 SBOM、Provenance、簽名與漏洞掃描混成一個欄位。
- 信任所有 CI Runner,忽略自託管 Runner 遭入侵後的撤銷與影響範圍。
- 金鑰輪換後讓所有歷史制品失效,或永遠接受已撤銷身分。
- 驗證服務故障時無條件放行,讓降級成為繞過策略。
- 只保存通過或拒絕,沒有策略版本、原因碼與可重播證據。
追問及應對
如何證明建置器沒有在聲明中說謊?
Provenance 只能證明受信建置流程發布某項聲明,不能自動證明每一步誠實。用隔離建置器、最小權限、可重現或可比較建置、獨立日誌和策略約束降低信任假設;高風險制品再要求第二方複核或額外證明。
如果制品倉庫被攻擊,驗證鏈還能工作嗎?
部署按摘要拉取並驗證簽名,倉庫標籤和中繼資料被改不會改變已簽摘要。證明和簽名包保留獨立不可變副本;發現入侵後凍結新發布、比較日誌與摘要、撤銷受影響身分,並重新驗證部署環境。
如何支援多個建置系統與供應商?
用統一聲明 schema 和身分映射層,把 GitHub Actions、自託管 Runner 與供應商建置器映射到策略可識別的工作流身分。適配器只產生規範化聲明,驗證器仍執行同一摘要、身分、時間和策略檢查。
業務團隊抱怨驗證拖慢發布,如何取捨?
先測驗證耗時、快取命中和失敗原因,優化熱索引與並行讀取,不降低信任邊界。低風險環境可提供短時可稽核快取;生產保留強驗證,並用影子模式確認拒絕率來自真缺陷還是策略誤配。例外要有期限、負責人與自動到期。