クラウド Mac チーム署名証明書運用:fastlane match とヘッドレス Keychain 解錠
深夜2時、チームで3人目のメンバーが同じクラウド Mac 上で fastlane build を実行したところ、CI がいきなりエラーを返した。証明書が無効だという。3人はそれぞれ自分のローカルで match を実行していたが、誰も気づかないうちに、自分の操作が他のメンバーが直前にインポートした署名証明書をデフォルトの Keychain から追い出してしまっていた。複数人が1台のクラウド Mac を共有してビルドを出す運用では、この事故はほぼ避けられない罠だ。本稿では、fastlane match を使って証明書を一元管理する方法と、ヘッドレスなリモートセッションで Keychain が何度もパスワードを要求してくる問題の解決策を記録する。
問題:クラウド Mac を共有すると証明書が必ず崩れる理由
ローカル開発では、1台の Mac は通常1人にしか使われないため、証明書をデフォルトの Keychain に入れても誰とも競合しない。しかし日単位で借りるクラウド Mac はチームで共有されることが多い。今日はあなたがログインしてパッケージを作り、明日は同僚がリモートログインして設定を変えてもう一度ビルドする、といった具合だ。もし全員がそれぞれ fastlane match development を実行したり、手動で p12 をインポートしたりすると、デフォルトの Keychain 内の証明書エントリは何度も上書き・削除・再作成され、最終的に「証明書は存在するが署名時に秘密鍵が見つからない」という典型的なエラーが発生する。
根本原因は、本来別々に扱うべき2つの事柄が混在してしまっていることにある。証明書の保管と配布、そして証明書のローカル Keychain における一時的な認可だ。前者は一元管理すべきものであり、後者こそセッションごとに分離すべきものである。
fastlane match の核心的な考え方
match は開発用証明書・配布用証明書とそれに対応するプロビジョニングプロファイルを暗号化し、専用の Git リポジトリにまとめて保存する。チームの全員(およびクラウド Mac 上の自動化ユーザー)はこのリポジトリから同じ証明書を取得するのであって、各自が Apple Developer の管理画面で再生成するわけではない。これにより、同じ Bundle ID がどのマシンでも同じ署名結果になることが保証され、「証明書数が Apple の上限を超える」という古くからの問題も回避できる。
match の価値は「証明書を自動生成すること」ではなく、「もう誰も自分で証明書を生成する必要がなくなること」にある——この一文が、match のあるべき使い方を決める。書き込み操作を行うのは1人(あるいは1回の CI 初期化タスク)だけにし、それ以外のすべての場面では読み取り専用モードで取得すべきだ。
クラウド Mac 上での証明書リポジトリ初期化
クラウド Mac 上のプロジェクトディレクトリで、まず Matchfile が公開アドレスではなくプライベートリポジトリを指していることを確認する。
git_url("git@github.com:your-org/certs-private.git")
storage_mode("git")
type("appstore")
初回の初期化は、信頼できる1台のマシン上で1回だけ実行する。
fastlane match appstore --readonly false
以降、クラウド Mac 上でのすべての取得操作には --readonly true を付け、既存の証明書のみを読み取り、再生成を試みないようにする。
fastlane match appstore --readonly true
デプロイ用ユーザーには読み取り専用の Git デプロイキーを個別に発行し、チームメンバー個人の SSH キーをクラウド 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 の手順は見落としやすい。これを忘れると、Keychain がすでに解錠されていても codesign は依然として認可を求めるダイアログを出す。この専用 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 内のプロビジョニングプロファイルは、タスク終了時にまとめて削除することを推奨する。次のレンタル期間で自動的にクリーンな環境が引き継がれることは期待できない。具体的な機種やノードがチームの並行出荷ニーズを満たすかどうかは、コンソールで現在選択可能な構成を確認したうえでスケジュールを組むことを推奨する。チップ機種の違いによるコンパイル速度の差は、複数人が順番待ちする時間に直接影響する。
チーム連携と権限境界
責任範囲を明確に切り分けることで、後々のトラブルシューティングにかかる時間を大幅に削減できる。
- 証明書の書き込み権限:1人、あるいは1本の初期化パイプラインだけに与える。日常のメンバーとクラウド Mac 上の自動化アカウントは、すべて
--readonly trueを使う。 - リポジトリのアクセス権限:証明書リポジトリは個人アカウントのキーではなくデプロイキーを使い、人単位・マシン単位で個別に無効化できるようにする。
- ローカル Keychain の分離:利用者ごと、あるいは並行するビルドラインごとに独立した Keychain ファイルと独立したビルドディレクトリを割り当て、デフォルトの Keychain を互いに上書きしないようにする。
- パスワードとトークンの保管:
KEYCHAIN_PWD、P12_PWD、Git デプロイキーはいずれも環境変数や CI の暗号化変数経由で注入し、スクリプトファイル自体には書き込まない。
リリース前チェックリスト
- [ ]
Matchfileがプライベートリポジトリを指しており、チームの誰も過去に公開リポジトリに証明書履歴を保存していないこと - [ ] クラウド Mac 上で実行するすべての match コマンドに
--readonly trueが付いていること - [ ] 専用の署名 Keychain に
set-key-partition-listが実行済みで、codesignがダイアログを出さないことを確認済みであること - [ ] 並行する利用者/ビルドラインごとに独立した Keychain ファイルとビルドディレクトリがあること
- [ ] タスクまたはレンタル期間終了前に、専用 Keychain と一時プロビジョニングプロファイルを削除するクリーンアップスクリプトがあること
証明書管理と Keychain 解錠という2つの事柄を分離して扱うようにすれば、チームが同じクラウド Mac 上で交代しながらパッケージを出しても、お互いを踏み潰す事故は基本的に発生しなくなる。新しいメンバーがプロジェクトに参加する際も、リポジトリを一度取得して読み取り専用コマンドを1回実行するだけでよく、証明書を新たに申請する必要はない。
よくある質問
match の証明書リポジトリは公開 Git リポジトリでも良いですか?
推奨しません。match は p12 証明書とプロビジョニングプロファイルを暗号化して保存しますが、公開リポジトリでも構造やメタデータは露出します。非公開リポジトリを使い、クラウド Mac 上のデプロイ用ユーザーには読み取り専用トークンだけ発行してください。
クラウド Mac を再起動すると毎回 Keychain のパスワードを入力する必要がありますか?
不要です。security create-keychain で固定パスワードの専用署名用 Keychain を作成し、security set-keychain-settings で自動ロックを無効化、-A オプションで codesign のアクセスを承認しておけば、セッション再ログイン後に解錠スクリプトを一度実行するだけで済みます。
複数人が同じクラウド Mac でビルドすると証明書は競合しますか?
はい、各自が match を実行するとデフォルトの Keychain へのインポート・エクスポートが繰り返され競合します。各メンバーに専用のビルドディレクトリと専用 Keychain ファイルを割り当て、match の readonly モードで既存証明書を読み取るだけにすると競合を避けられます。
HireVPS(HireVPS)
今すぐ専有クラウド Mac mini を試してみましょう
日単位でレンタル可能、2分で SSH/VNC の認証情報を取得、プランはいつでもアップグレードできます。