GitHub Actions で複数のプライベートリポジトリにSSHアクセスする方法
発生した現象
エラー内容
composer require で複数のプライベートリポジトリ(VCS)からパッケージをインストールする際、GitHub Actions で以下のエラーが発生した。
Failed to download your-org/private-package from dist: HTTP/2 404
Now trying to download from source
Error: Failed to execute git clone --mirror -- git@github.com:your-org/private-package.git
fatal: Authentication failed for 'https://github.com/your-org/private-package.git/'
問題の原因
- プライベートリポジトリのzipballはGitHub API経由でアクセスできないため、distダウンロードが失敗した(404エラー)
- SSH認証の問題により、sourceダウンロードも失敗した
SSHデプロイキーの制限
複数のデプロイキーを webfactory/ssh-agent で登録しても、片方のリポジトリにしかアクセスできない現象が発生する。
- uses: webfactory/ssh-agent@v0.9.1
with:
ssh-private-key: |
${{ secrets.FIRST_KEY }}
${{ secrets.SECOND_KEY }}
原因は、GitHubがどの登録済みキーでもSSH認証を受け入れる一方で、Deploy Keyは特定のリポジトリにのみアクセス権を持つことにある。SSHクライアントが最初に試したキーで認証が成功しても、そのキーが対象リポジトリへのアクセス権を持たなければエラーとなる。
| キーの順番 | 認証先リポジトリ | 結果 |
|---|---|---|
| KEY_A が先 | repo-a | repo-a ✅ / repo-b ❌ |
| KEY_B が先 | repo-b | repo-a ❌ / repo-b ✅ |
解決策
webfactory/ssh-agent の自動設定機能を活用
webfactory/ssh-agent は、SSHキーのコメントにリポジトリURLが含まれている場合、自動的に以下を生成する。
- SSH config(ホストエイリアス)
- Git URL rewrite 設定
これにより、正しいキーが自動的に選択される。
手順
1. SSHキーを生成(コメントにリポジトリURLを指定)
# リポジトリAのキー
ssh-keygen -t ed25519 -C "git@github.com:org/repo-a.git" -f ./repo_a_key -N ""
# リポジトリBのキー
ssh-keygen -t ed25519 -C "git@github.com:org/repo-b.git" -f ./repo_b_key -N ""
ここで重要なのは、-C オプションで git@github.com:org/repo.git 形式のURLを指定することである。
2. デプロイキーを登録
各リポジトリの Settings → Deploy keys に公開鍵を登録する。
cat ./repo_a_key.pub # → repo-a のデプロイキーに登録
cat ./repo_b_key.pub # → repo-b のデプロイキーに登録
3. GitHub Secrets を更新
秘密鍵をGitHub Secretsに登録する。
cat ./repo_a_key # → REPO_A_SSH_KEY
cat ./repo_b_key # → REPO_B_SSH_KEY
4. ワークフローの設定
- name: Set up SSH agent
uses: webfactory/ssh-agent@v0.9.1
with:
ssh-private-key: |
${{ secrets.REPO_A_SSH_KEY }}
${{ secrets.REPO_B_SSH_KEY }}
ワークフロー側の追加設定は不要。webfactory/ssh-agent がキーコメントを読み取り、自動的に設定を生成する。
参考
webfactory/ssh-agent ドキュメントより
Deploy Keysを複数使用する際の課題があります。GitHubサーバーは最初の既知キーを受け入れますが、各キーは単一のリポジトリに限定されるため、間違ったキー/リポジトリの組み合わせが試されるとエラーになります。
このアクションはこの問題を解決するため、キーコメントをスキャンして自動的にGitとSSH設定を生成します。
- デプロイキー作成時にコメントに「
git@github.com:owner/repo.git」のようなURLを指定- アクション実行時にキーコメントがスキャンされる
- URLを含むコメントについて、
url.<base>.insteadofを使用したGit設定が自動生成される- 対応するSSH設定セクションが生成され、正しいキーでGitHub接続を完成させる
まとめ
| 方法 | 複数リポジトリ対応 | 設定の手間 |
|---|---|---|
| デプロイキー(コメントなし) | ❌ | 低 |
| デプロイキー(URLコメント付き) | ✅ | 低 |
| Personal Access Token (PAT) | ✅ | 低 |
| 手動SSH config設定 | ✅ | 高 |
SSHキー生成時に -C "git@github.com:org/repo.git" でコメントを指定する方法が最もシンプルでおすすめである。
補足
認証方式の比較
CI/CD環境でのプライベートパッケージ認証には、主に3つの方法がある。
| 認証方式 | 紐づき先 | 退職リスク | トークン有効期限 |
|---|---|---|---|
| PAT | 個人アカウント | あり | 設定可能(最大無期限) |
| SSH Deploy Key | リポジトリ | なし | なし |
| GitHub App | Organization | なし | 短期間(自動更新) |
SSH Deploy Keyを選択した理由
SSH Deploy Keyを使う利点は以下の通りである。
- Deploy keyはリポジトリに紐づくため、メンバーの退職などの個人に依存しない
-
git@github.com:...形式をそのまま使え、composer.jsonの変更が不要 - 少数のリポジトリであれば設定がシンプル
ただし、SSH Deploy Keyには有効期限がないため、セキュリティ上は定期的なローテーションを推奨する。
PATの制約
PATは個人のGitHubアカウントに紐づく。これはGitHubの仕様であり、以下のような運用上の問題がある。
- 特定の誰かのアカウントで生成する → その人が退職/異動したら無効化リスク
- CI/CD専用の共有アカウントを作る → GitHubの利用規約では1人1アカウントが原則
GitHub AppとSSH Deploy Keyの使い分け
| 観点 | SSH Deploy Key | GitHub App |
|---|---|---|
| 適したスケール | 少数のリポジトリ | 多数のリポジトリ |
| 権限の粒度 | リポジトリ単位(read/write) | 細かいスコープ制御 |
| セキュリティ | キーの定期ローテーションを推奨 | トークンが短命で自動更新 |
| 監査ログ | 限定的 | 詳細 |
GitHub Appはトークンが短命で自動更新されるため、セキュリティ面では優れている。一方で、初期設定の複雑さやApp登録の手間がある。
今回は2つのプライベートパッケージという小規模な構成であり、SSH Deploy Keyの方が運用コストとのバランスが良いと判断した。リポジトリ数が増えた場合や、より厳密な権限管理が必要になった場合はGitHub Appへの移行を検討すべきである。
Discussion