雲端 Mac 團隊簽名憑證管理實戰:fastlane match 與遠端 Keychain 免密解鎖

Security ·約 7 分鐘閱讀

雲端 Mac 團隊簽名憑證管理實戰:fastlane match 與遠端 Keychain 免密解鎖

凌晨兩點,團隊裡第三個人在同一台雲端 Mac 上跑了一次 fastlane build,結果 CI 直接報錯:證書失效。三個人各自在本地跑過 match,誰也沒注意到自己那一次操作,已經把別人剛匯入的簽名證書從系統預設 Keychain 裡擠了出去。這種事故在多人共用一台雲端 Mac 出包時幾乎是必經的坑——本文記錄我們如何用 fastlane match 把證書統一管起來,並解決無頭遠端連線裡 Keychain 反覆要密碼的麻煩。

問題:共用一台雲端 Mac,證書為什麼總出岔子

本機開發時,一台 Mac 通常只服務一個人,證書裝進預設 Keychain 誰都不會打架。但一台按天租來的雲端 Mac 常常是團隊共用:今天你連上去打包,明天同事遠端登入改個設定又跑一次。如果每個人都各自執行 fastlane match development 或手動匯入 p12,系統預設 Keychain 裡的證書條目就會被反覆覆蓋、刪除又重建,最終出現「證書存在但簽名時找不到私鑰」這種典型報錯。

根本原因是兩件事被混在一起了:證書的儲存與分發,和證書在本機 Keychain 裡的暫時授權。前者應該集中管理,後者才需要按會話隔離。

fastlane match 的核心思路

match 把開發證書、發行證書和對應的描述檔加密後統一存進一個獨立的 Git 儲存庫,團隊所有人(以及雲端 Mac 上的自動化使用者)都從這個儲存庫拉取同一份證書,而不是各自去 Apple Developer 後台重新產生。這樣能保證同一個 Bundle ID 在任何機器上簽名結果一致,也避免了「證書數量超過 Apple 限額」的老問題。

match 的價值不是「自動產生證書」,而是「不再需要每個人自己產生證書」——這一句話決定了它該怎麼被使用:只應該有一個人(或一次 CI 初始化任務)執行寫入操作,其餘所有場景都用唯讀模式拉取。

在雲端 Mac 上初始化證書儲存庫

在你的雲端 Mac 專案目錄下,先確認 Matchfile 指向私有儲存庫而不是公開位址:

git_url("git@github.com:your-org/certs-private.git")
storage_mode("git")
type("appstore")

首次初始化只在一台受信任的機器上執行一次:

fastlane match appstore --readonly false

之後所有雲端 Mac 上的拉取操作都加 --readonly true,確保它們只讀取既有證書、不會嘗試重新產生:

fastlane match appstore --readonly true

給部署使用者單獨發一個唯讀的 Git 部署金鑰(deploy key),而不是把團隊成員的個人 SSH key 都塞進雲端 Mac,權限邊界更清楚,人員異動時也不用逐個撤銷。

無頭遠端連線裡的 Keychain 免密解鎖

雲端 Mac 大多是透過 SSH 或 VNC 遠端登入,不是本人親自坐在螢幕前打密碼,系統預設的登入 Keychain 在連線之間經常處於鎖定狀態,codesign 一執行就跳出授權對話框——這在無人值守的 CI 觸發場景裡會直接卡死。

做法是新建一個專用的簽名 Keychain,和登入 Keychain 分開:

security create-keychain -p "$KEYCHAIN_PWD" signing.keychain
security set-keychain-settings -lut 21600 signing.keychain
security unlock-keychain -p "$KEYCHAIN_PWD" signing.keychain
security import cert.p12 -k signing.keychain -P "$P12_PWD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PWD" signing.keychain
list=$(security list-keychains -d user | tr -d '"')
security list-keychains -d user -s signing.keychain $list

其中 set-key-partition-list 這一步很容易漏掉,漏了的話 codesign 依然會跳窗要求授權,即便 Keychain 已經解鎖。這台專用 Keychain 建議只保留簽名需要的證書,不要和日常密碼混放,退租或轉交專案時刪掉它比清理預設 Keychain 乾淨得多。

CI 觸發後的證書拉取與清理

自動化流程建議按下面的順序執行,每一步的失敗都應該讓流水線直接停下,而不是繼續往後跑:

步驟 命令/動作 失敗時的處理
解鎖專用 Keychain security unlock-keychain 立即中止,報工單而非靜默重試
唯讀拉取證書 fastlane match … --readonly true 中止,提示先在主控機執行寫入
執行建置 xcodebuild archive 保留建置日誌供排查
清理暫時 Keychain 參照 security delete-keychain signing.keychain(按需) 記錄清理是否成功

如果雲端 Mac 是按天租用、用完就會轉手給其他任務或到期回收,最後一步的清理尤其重要:專用簽名 Keychain、暫時下載的 p12、~/Library/MobileDevice/Provisioning Profiles 裡的描述檔都建議在任務結束時一併清空,不要指望下一個租期自動繼承乾淨環境。具體機型與節點是否滿足你團隊並發出包的需求,建議在控制台確認目前可選配置後再排期,不同晶片型號的編譯速度差異會直接影響多人排隊等待時長。

團隊協作與權限邊界

把職責拆清楚能省掉後續很多故障排查時間:

上線前檢查清單

把證書管理和 Keychain 解鎖這兩件事分開處理之後,團隊在同一台雲端 Mac 上交替打包基本不會再互相踩坑,新人加入專案時也只需要拉一次儲存庫、跑一遍唯讀命令,不用重新申請證書。

常見問題

match 的憑證庫能直接用公開的 Git 儲存庫嗎?

不建議。match 會把 p12 憑證與描述檔加密後存進儲存庫,雖然加了密碼保護,但公開儲存庫仍會暴露結構與元資料,應使用私有儲存庫,並給雲端 Mac 上的部署帳號單獨發只讀 token。

雲端 Mac 重新啟動後 Keychain 密碼還需要每次手動輸入嗎?

不需要。用 security create-keychain 搭配固定密碼建立專用簽名 Keychain,再用 security set-keychain-settings 關閉自動鎖定,配合 -A 授權 codesign 免確認存取,重新登入連線後執行一次解鎖腳本即可。

多人同時用同一台雲端 Mac 打包,憑證會互相覆蓋嗎?

會,如果各自跑 match,預設 Keychain 裡的憑證會被反覆匯入匯出導致衝突。做法是替每個人分配獨立建置目錄與專用 Keychain 檔,match 的 readonly 模式只讀取儲存庫憑證而不重新產生,避免互相覆蓋。

HireVPS

今天就試試獨享雲端 Mac mini

按日起租,2 分鐘取得 SSH/VNC 憑證,配置隨時可升級。

立即租用 Mac mini