後端面試題:如何安全設計 GitHub Actions 可重用工作流程?
題幹與適用場景
團隊把建置、測試和發布流程抽成跨儲存庫可重用工作流程。請設計呼叫方與被呼叫方之間的輸入、輸出、密鑰、GITHUB_TOKEN 權限和版本引用契約,並說明如何防止權限擴大、供應鏈漂移和上下文誤用。
面試官考察點
- 是否理解可重用工作流程是在 job 層級呼叫,而非普通 step。
- 是否能說明呼叫方傳入的
GITHUB_TOKEN權限只能被被呼叫方降級,不能升級。 - 是否能最小化
secrets、環境變數和github上下文暴露面。 - 是否能處理巢狀層數、呼叫上限、版本固定和組織策略。
回答前需要釐清的問題
- 工作流程跨儲存庫、跨組織還是同儲存庫重用?可見性和策略允許哪些來源?
- 被呼叫流程只建置製品,還是擁有部署、發布或寫回儲存庫的權限?
- 密鑰是逐項傳遞、組織級繼承,還是需要環境保護規則?
- 版本更新要追蹤分支、發布標籤還是不可變提交引用?
30 秒回答框架
我會先定義窄介面:workflow_call 宣告型別化輸入、逐項 secrets 和必要輸出;呼叫 job 明確設定最小 permissions,被呼叫工作流程再次收緊權限。發布工作流程與建置工作流程分離,敏感環境由環境保護規則控制。引用使用經審查的提交或受控標籤,並限制組織允許的 actions 與 reusable workflows。最後用稽核日誌、失敗重跑和權限回歸測試驗證契約。
分步驟深入解答
1. 可重用工作流程的呼叫邊界
可重用工作流程由 workflow_call 觸發,呼叫方在一個 job 上使用 uses。呼叫 job 只能使用文件允許的鍵,例如 with、secrets、permissions、strategy 和 needs;不能把它當作普通 step 注入任意命令。
2. 輸入、輸出和型別約束
在被呼叫工作流程中宣告必填輸入、預設值和型別,讓呼叫方契約在解析階段失敗。輸出只暴露發布所需的製品摘要、版本或結果狀態,不返回令牌、完整日誌或內部路徑。
3. 密鑰逐項傳遞
優先使用明確 secrets 對映,只把一個部署動作需要的密鑰傳入。secrets: inherit 會傳遞呼叫方可存取的全部密鑰,適合邊界明確的同組織場景,卻會擴大稽核和洩漏面,跨信任域時應避免。
4. GITHUB_TOKEN 權限降級
呼叫 job 應明確宣告 permissions,例如建置只讀取程式碼和寫入製品。GitHub 文件規定,被呼叫工作流程繼承的 GITHUB_TOKEN 權限只能相同或更嚴格,不能由被呼叫方提升。因此高權限動作必須由呼叫方明確授予,並在程式碼審查中記錄理由。
5. github 上下文歸屬
被呼叫工作流程中的 github 上下文關聯呼叫方工作流程。不要假設被呼叫儲存庫的分支、事件或權限自動取代呼叫方資訊;需要來源儲存庫、提交和觸發者時,明確傳入並在日誌中脫敏。
6. 引用和供應鏈漂移
工作流程可引用分支、標籤或提交。分支會隨時間移動,標籤也可能被重新指向;生產路徑應使用經審核的不可變提交,或由組織策略限制允許的引用。GitHub 的 actions 策略還可要求 action 使用完整提交 SHA,並限制可用來源。
7. 巢狀、矩陣和併發
巢狀可重用工作流程最多十層,單個工作流程檔案最多連接五十個唯一可重用工作流程。矩陣可呼叫可重用工作流程,但要為每個組合設定資源上限、併發組和取消策略,避免重複部署或互相取消。
8. 稽核、重跑和回滾
記錄呼叫工作流程的儲存庫、提交、輸入摘要、權限宣告和製品摘要。重跑全部任務可能重新解析引用;只重跑失敗任務可能沿用第一次嘗試的提交,因此稽核記錄必須包含實際解析到的版本。回滾應切換到已驗證引用並撤銷高權限環境授權。
設計取捨與邊界
inherit減少設定,卻把密鑰邊界變成隱式契約;跨組織或多租戶平台應明確對映。- 固定提交提高可重現性,但升級需要自動化更新、審查和回滾視窗。
- 把部署封裝進通用工作流程可減少重複,卻可能隱藏環境保護規則;生產部署應保留清晰的審批邊界。
- 令牌權限解決 API 授權,不等於執行器上的檔案、網路和第三方 action 已可信;仍需隔離不受信程式碼。
落地計畫與證據
- 為
workflow_call編寫輸入、輸出和逐項 secrets 契約,拒絕未宣告欄位。 - 對每個呼叫 job 生成權限矩陣,驗證被呼叫方不能升級
GITHUB_TOKEN。 - 將生產引用固定到審核提交,配置組織允許清單並檢查 SHA 策略。
- 用假密鑰和受限儲存庫執行拉取請求、跨儲存庫呼叫、巢狀和重跑演練。
- 對照 GitHub 的重用工作流程、工作流程語法和 Actions 設定文件複核實作。
常見誤區與追問
誤區一:把 reusable workflow 當作 action step
它在 job 層級呼叫,支援的欄位和上下文不同。把 step 語法直接搬過去會導致解析失敗或權限假設錯誤。
誤區二:預設使用 secrets.inherit
繼承會把呼叫方可存取的全部密鑰交給被呼叫工作流程。除非信任邊界和組織範圍明確,否則應逐項傳遞。
誤區三:只在被呼叫方設定高權限
被呼叫方不能提升呼叫方傳入的令牌權限。高權限必須在呼叫 job 中明確宣告,並透過審查和稽核保留理由。
追問:為什麼標籤引用仍有風險?
標籤可以移動,重跑或未來執行可能解析到不同提交。生產鏈路應固定審核提交,或由組織策略限制可用引用。
追問:如何驗證密鑰沒有洩漏?
使用假密鑰和最小權限執行完整鏈路,檢查日誌、輸出、製品和錯誤路徑;同時稽核 inherit、環境變數和第三方 action 的讀取範圍。