GitLab 官方指出,Shell executor 的工作可能存取同一部主機上其他專案的程式碼;這代表「標籤路由正確」不等於「主機已完成安全隔離」。因此,你應在本週先建立共享建置池+專用發布池的雙層架構:可信專案的普通編譯與測試可以共用 Mac Runner,生產簽名、不同信任域專案及非可信合併請求則改用獨立 Mac Runner。GitLab Shell executor 的安全說明已明確限制其適用範圍。

這篇文章適合三類人:需要為多個倉庫統一提供 GitLab macOS 建置資源的平台工程負責人;需要限制憑證、私密金鑰及內部依賴存取範圍的資安與發布負責人;以及正在評估共享、專用或彈性遠端 Mac 節點數量的 IT 與採購決策者。

准入時間線:先分可信度,再決定共享範圍

雙層架構與三種任務類型

共享硬體不代表共享身份,也不代表共享憑證。接入任何專案前,先按四項條件審查:程式碼來源、倉庫權限、內部網路存取範圍,以及簽名資產的敏感程度。

  • 可進入共享建置池:內部可信倉庫、受控分支、只需普通依賴下載,不接觸生產簽名資產。
  • 需要獨立服務帳號或 Runner:同一企業內但屬於不同業務部門,或需要不同內部網路權限與快取範圍。
  • 必須使用獨立 Mac:生產簽名、App Store Connect 憑證、私密金鑰,或來源不可信的合併請求。

GitLab Runner 的範圍可分為 project、group 與 instance。企業多專案共享時,先從受控的 group runner 開始,將允許的專案清單維持在群組邊界內,不要一開始就把建置節點開放給整個 instance。GitLab Runner 範圍管理文件說明了不同範圍對專案可見性及管理責任的影響。

第一小時:用標籤與受保護規則建立路由基線

GitLab CI 多專案共用 Mac Runner 的路由設定

標籤只負責「工作送去哪裡」,不負責「工作在主機上能讀什麼」。共享池可使用例如 macos-build 的標籤,發布節點則使用更嚴格的 macos-release 標籤,並設定只執行帶有標籤的工作。

在 GitLab Runner 設定中,應同步完成以下項目:

  1. 共享 Runner 綁定受控 group,而不是直接使用 instance runner。
  2. 啟用 Protected Runner,只接受受保護分支或受保護標籤的任務。
  3. 普通建置與正式發布使用不同標籤,不能只靠工作名稱區分。
  4. 建立允許專案清單,記錄每個專案能進入哪個 Runner。
  5. 用一條普通建置工作及一條發布工作做拒絕路由測試。

GitLab 官方也將 runner tag、protected runner 與分支控制列為工作路由及存取控制機制;它們不是完整的主機沙箱。Runner 設定與標籤說明可作為驗收基準。

最小化的 YAML 只需保留路由和驗證所需內容:

build_ios:
  tags:
    - macos-build
  script:
    - whoami
    - pwd
    - xcodebuild -version

不要在第一輪驗收時加入複雜快取、簽名或部署步驟。你要先證明一般專案會進入共享池,而發布工作不會因標籤錯誤落到普通節點。GitLab YAML 語法文件可用來核對工作與標籤的寫法。

首條流水線:拆開帳號、工作區與可重建資料

GitLab Runner、macOS 本機帳號與服務帳號

在管理員終端機中成功執行,不代表 CI 工作真的安全。首條流水線必須在 GitLab Runner 服務帳號下驗收,記錄 whoami、家目錄、工作目錄、暫存目錄及使用者級設定檔的實際歸屬。

你應將資料分成四類:

  • 專案原始碼:工作完成後清除,不作為長期資料保存。
  • 可重建快取:只保存依賴或中間產物,並按專案或信任域分開。
  • 建置歸檔與制品:交由 GitLab 制品保存機制管理,不放在永久共享目錄。
  • 長期憑證:不放入專案工作區,也不與普通建置共用。

GitLab 的快取文件提醒,快取是為了重用依賴與中間資料,不應直接當成機密保管庫。CI/CD 快取官方說明可協助你分辨快取、制品及工作目錄的責任邊界。

macOS Shell executor 的實際隔離邊界

Shell executor 會在主機本地環境執行工作。即使你拆分了 ~/builds~/cache 與暫存目錄,同一個 macOS 本機帳號下的程式仍可能讀取其他可見路徑、環境變數、背景處理程序或使用者設定。目錄拆分可以降低誤用,不能取代主機級隔離。

首條流水線應完成五項操作:

  1. 由 CI 印出實際服務帳號與工作路徑。
  2. 建立一個測試檔案,確認任務結束後是否被清除。
  3. 檢查工作區、快取及暫存檔的擁有者與權限。
  4. 嘗試讀取另一個測試專案的工作目錄,確認是否被拒絕。
  5. 保存清理前後的檔案清單及 Runner 工作紀錄。

只要跨專案讀取測試成功,就不要把該節點描述為「已隔離」。應改為獨立 Runner 服務帳號;若信任域仍不同,則直接改用獨立 Mac 節點。

第二個專案:用交叉污染測試決定是否繼續共享

多專案接入與殘留資產

第一個專案成功後,不要立即批量開放共享池。第二個專案接入時,主動檢查以下五種殘留:

  • 前一個專案的原始碼、建置歸檔及環境變數。
  • 未結束的背景處理程序與鎖定檔。
  • DerivedData、模擬器資源及 Xcode 使用者設定。
  • Homebrew 狀態、依賴快取與自訂工具鏈。
  • GitLab 制品、工作區及暫存檔。

你應安排一個「專案 A 建立標記、專案 B 嘗試尋找、清理後再次驗證」的測試。測試重點不是能否成功編譯,而是 B 是否能看到 A 不應暴露的內容。若兩個專案屬於不同部門、不同客戶或不同內部網路信任域,這一階段就應停止共享。

GitLab Runner 多專案權限選擇

「GitLab Runner 可以同時給多個專案使用嗎」的答案是:可以,但前提是專案彼此可信,而且你接受 Shell executor 的有限隔離。對不同信任域,group runner 不應被當作安全邊界;project runner 只能縮小註冊範圍,仍不能消除同一台 Mac 的主機風險。

「GitLab group runner 和 project runner 應該怎麼選」可以按以下原則處理:

  • 多個同信任域、相同清理政策的專案:選受控 group runner。
  • 只有一個專案,或需要獨立維護與審計:選 project runner。
  • 生產簽名、客戶隔離或非可信程式碼:不用共享 runner,改用獨立 Mac。

生產放量:把 Keychain 與簽名任務移出共享池

簽名身份與普通建置分離

生產憑證私密金鑰、Provisioning Profile、App Store Connect 憑證及普通依賴存取令牌,應分別定義保存與呼叫邊界。macOS Keychain 的存取控制可以限制項目使用條件,但不能抵消同一 Runner 使用者執行任意腳本所帶來的風險。Apple Keychain Services 文件存取控制清單文件都應納入你的驗收依據。

「共享 Mac Runner 如何防止 Keychain 和快取洩露」不能只靠清空快取。可執行的做法是:

  1. 共享建置節點完全不放生產私密金鑰。
  2. 專用發布節點只接受 Protected Runner 任務。
  3. 簽名工作只允許受保護分支或標籤觸發。
  4. 以最小權限建立 Keychain 項目的存取控制。
  5. 在節點重啟後重新驗證 Keychain 可用性及拒絕規則。
  6. 保存憑證輪換、撤銷及未授權專案拒絕紀錄。

Apple 對 Keychain 項目可見性及可存取條件另有限制項目存取的官方說明。因此,「iOS 正式簽名是否需要專用 GitLab Runner」的實務答案通常是需要:不是因為 Xcode 本身要求每個專案一台 Mac,而是因為正式簽名身份不應與普通建置及非可信程式碼共用執行邊界。

首週里程碑:用證據決定共享、拆分或擴容

驗收項目與成本責任

完成首週運行後,不要只看 Runner 顯示在線。你至少要保存程式碼拉取、Xcode 建置、制品上傳、簽名及故障恢復的閉環紀錄。所有排隊、並發衝突、清理結果、重啟恢復及簽名占用時間,都應引用企業自己的流水線紀錄;沒有紀錄就不要寫成性能或容量結論。

驗收階段 共享建置池 專用發布池 必須保存的證據
路由 macos-build macos-release 標籤匹配及拒絕路由紀錄
身份 非生產服務帳號 專用服務帳號及本機帳號 whoami、路徑及權限輸出
工作區 專案級清理及快取 發布後清理及更嚴格保留政策 清理前後檔案清單
憑證 不存放生產私密金鑰 僅供受保護任務使用 Keychain 存取及輪換紀錄
恢復 驗證普通建置可重跑 驗證簽名邊界可重建 重啟後完整流水線紀錄
觀察結果 判定 下一步
專案同信任域,無跨專案讀取,清理穩定 繼續共享 保持 group runner,按週期抽測
不同部門或出現快取、工作區污染 拆分 改用獨立 Runner 服務帳號或獨立 Mac
生產簽名與普通建置共用 立即拆分 建立只接受 Protected 任務的發布節點
排隊與資源衝突持續出現 擴容 增加共享基礎容量或導入彈性遠端 Mac
重啟後路由、清理或簽名失效 暫停放量 先完成恢復驗收,再重新開放專案
企業條件 建議架構 不建議做法
可信內部專案、無生產憑證 共享 Mac 建置池 將共享池宣稱為安全沙箱
多個信任域、內部依賴不同 分信任域配置獨立節點 只增加 runner tag
生產簽名與 App Store Connect 發布 專用發布 Mac 把私密金鑰放入普通建置節點
峰值需求不固定 共享基礎容量+彈性遠端 Mac 為短期峰值一次購買大量硬體
非可信合併請求 隔離節點或拒絕進入共享池 讓其直接使用 Shell executor

共享或專用:本週的決策條件

  • 所有專案均為同一信任域、來源受控,且交叉污染測試沒有讀取結果,選受控 group runner;否則回退到 project runner 或獨立 Mac。
  • 工作只做普通編譯與測試,且不接觸生產 Keychain,可進共享建置池;否則移至專用發布池。
  • 非可信合併請求需要執行任意腳本,不要讓它進入含有其他專案資料的 Shell executor。
  • 單台現有 Mac 同時承載普通建置、非可信分支與正式簽名,先申請兩個隔離試點,不要直接批量採購。
  • 需求只在發布高峰出現,以共享基礎容量搭配彈性遠端 Mac 驗證;是長期穩定高負載,才評估自購專用硬體的 TCO。

你可以先閱讀 VMSPIN 的繁體中文服務入口,再用 Mac 遠端租賃價格頁整理試點所需的週期與節點數量。這裡的重點不是把所有 CI 工作搬到雲端,而是先讓共享建置與專用發布各自承擔清楚的風險。

如果你目前只有一台 Mac,讓它同時處理不同專案、非可信分支與正式簽名,缺點通常很具體:權限邊界混在同一個本機帳號,快取和 DerivedData 可能殘留,簽名憑證也會與任意 CI 腳本處於同一執行環境;遇到高峰時,普通測試還會與發布工作爭用節點。這種架構不適合直接擴大。對需要短期驗證、跨部門隔離或彈性增加節點的團隊,租用 VMSPIN 的遠端 Mac,先分別試跑共享建置節點與專用發布節點,通常比立刻採購多台實機更容易控制試點風險;至於長期穩定重負載或必須接駁實體周邊的場景,仍應把自購 Mac 納入正式 TCO 評估。