題目
設計一個多租戶 Secrets Rotation Service。租戶可以設定資料庫密碼、第三方 API 金鑰或憑證的輪換週期;服務負責產生新值、更新外部系統、寫入版本庫、逐步通知工作負載並停用舊值。請說明故障恢復、並發控制、稽核、權限和回滾方案。
面試官考察點
- 能否把輪換拆成可重試的狀態機,而不是無法恢復的排程腳本。
- 能否處理同一 secret 的並發任務、重複訊息和租戶之間的隔離。
- 能否設計雙版本或灰度發布,避免新值立即造成全量故障。
- 能否說明舊值清理、洩露應變、稽核證據與最小權限。
參考答案
核心物件包括 Secret、不可變 Version、RotationPolicy、Job 和 Lease。排程器按下一次輪換時間投遞任務;工作佇列按 secretid 分區,消費者用帶過期時間的租約取得所有權。資料庫唯一約束 (secretid, idempotency_key),讓重試不會生成兩次外部變更。
輪換狀態可採用 scheduled → generating → externalupdated → staged → rollingout → verified → retired,每個狀態保存外部請求識別、版本號和下次重試時間。先在外部系統建立新憑證,再寫入新版本並標記為 pending;工作負載透過版本引用或動態刷新逐步切換。健康檢查、錯誤率和授權測試通過後,才把新版本提升為 current。
控制面保存元資料,秘密值放在專用加密儲存中,應用只取得短時讀取權限。每次狀態變化寫入不可篡改稽核日誌;日誌中不出現秘密明文。回滾只切回仍有效的舊版本,不能把「撤銷舊憑證」當成回滾後的預設動作。
架構草圖
Scheduler -> Durable Queue -> Rotation Workers
| | |
Policy DB Lease/Idempotency External Provider
| |
Version Store + KMS Rollout Controller -> Workloads
|
Audit Log / Metrics / Alerts每個 worker 以租約續期,租約失效後其他 worker 才能接管。佇列訊息只攜帶 secret 識別和 job 識別;worker 從權限受限的版本儲存讀取值,避免把秘密放進訊息、日誌或指標標籤。
關鍵流程
- 排程器建立帶冪等鍵的 job,按租戶配額排隊。
- worker 取得租約,讀取目前版本和輪換策略;若 job 已完成則直接返回。
- 產生新值並呼叫外部 provider,使用 provider 的冪等請求號。
- 寫入 pending 版本,執行相容性檢查和小批量 rollout。
- 觀察錯誤率、認證成功率和健康探針;達到閾值後提升 current。
- 等待所有消費者確認,再停用並銷毀舊版本;失敗則重試或回滾。
每一步都持久化狀態和外部回應。重啟時從最後狀態繼續,而不是重新猜測外部系統是否已經更新。
常見誤區
- 只在 cron 中保存下次時間,程序重啟或重複觸發後無法恢復。
- 輪換完成就立即撤銷舊值,忽略連線池、快取和長連線的過渡時間。
- 把秘密值放入佇列、日誌、追蹤 span 或錯誤訊息。
- 用全域鎖阻塞所有租戶,或者沒有租戶配額導致單一租戶耗盡 worker。
- 把回滾理解為再次寫入舊值,卻不驗證舊憑證仍未過期或未被撤銷。
一致性與安全取捨
控制面可以使用強一致資料庫保存狀態機和唯一約束;通知與 rollout 使用至少一次投遞,因此消費者必須冪等。版本讀取可短暫快取,但 current 版本變更和撤銷事件必須有明確失效路徑。租戶權限應限制在自己的 secret、job 和稽核記錄,worker 只取得完成當前步驟所需的 provider 權限。
輪換週期不能只按固定時間決定,還要考慮金鑰類型、暴露風險、外部 provider 限制和恢復窗口。NIST 的金鑰管理建議強調使用期限與用途、保護等級和撤銷流程應共同決定策略。
用故障注入驗證:worker 在每個狀態前後崩潰、訊息重複、租約過期、provider 超時、部分實例 rollout 失敗、資料庫主從切換。斷言同一 job 不會產生重複外部憑證,最終只有一個 current 版本,舊值在確認窗口結束後才撤銷。再檢查租戶越權、稽核脫敏和告警延遲。
- AWS Secrets Manager 的
AWSPENDING/AWSCURRENT輪換流程:版本標籤和完成步驟。 - Google Cloud Secret Manager 輪換建議:重試、不可並發輪換、漸進 rollout 與舊版本清理。
- NIST SP 800-57 Part 1:金鑰用途、保護、使用期限和撤銷的管理原則。
追問
如何防止兩個 worker 同時輪換同一個 secret?
用資料庫租約或帶 fencing token 的分散式鎖,並把 token 寫入每次狀態更新。沒有目前 token 的 worker 即使恢復,也不能覆蓋新狀態;租約需要續期和明確過期時間。
provider 不支援冪等介面怎麼辦?
先在本地 job 持久化請求指紋和外部資源識別,重試前查詢 provider 的目前狀態。若無法查詢,就把該步驟標為需要人工確認,避免盲目建立多個憑證。
為什麼不能只讓應用讀取 latest?
latest 可能把未驗證的新值立即推給全部實例。使用不可變版本、分批 rollout 和健康檢查,可以把錯誤值的影響限制在小範圍並保留回滾點。
輪換任務積壓時如何保護系統?
按租戶和 provider 設定並發配額,使用優先級和退避重試,並暴露最老任務年齡、失敗率和剩餘恢復窗口。接近過期的 secret 可以提升優先級,但不能繞過權限和冪等檢查。
洩露事件發生時,輪換服務要做什麼?
立即凍結相關 job 的普通排程,建立高優先級緊急輪換,縮短 rollout 觀察窗口並保留取證日誌。撤銷舊值前確認所有關鍵消費者已切換,同時通知租戶和安全應變流程。