GitHub ActionsからS3にファイルをアップロードしたい。ECRにDockerイメージをプッシュしたい。こうした要件が出るたびに「IAMユーザーを作ってアクセスキーを発行し、GitHubのシークレットに登録する」という手順で対応してきた現場は多いはずです。
でも、このやり方には根本的なリスクがあります。長期有効なアクセスキーは一度漏洩すれば、攻撃者がそのキーを使って本番環境を好き放題に操作できてしまいます。
この記事では、アクセスキーを一切使わずにGitHub ActionsからAWSにアクセスできるようになるOIDCフェデレーションの仕組みと設定手順を、オンプレ環境でのSAML認証との対比を交えながら解説します。IAMロールの信頼ポリシー設計、ブランチ・環境別のロール分離、GitLab CIやCircleCIへの応用まで、実務で使える内容を網羅します。

なぜアクセスキー運用が危険なのか
オンプレ時代のサーバー間連携では「サービスアカウントのパスワードを1回作ったらずっと使う」という運用が珍しくありませんでした。CI/CDパイプラインでのアクセスキーは、まさにその発想をクラウドに持ち込んだものです。
問題は複数あります。
・誤コミット事故: `.env` ファイルやコード中にアクセスキーを混入させてしまうミスは今も頻繁に起きています。GitHubはコミット履歴を追跡しており、ファイルを削除しても過去の履歴からキーを拾われます。
・ローテーションの形骸化: アクセスキーは定期的にローテーションすべきですが、「動いてるから触りたくない」という心理が働き、何年も使い続けるケースは珍しくありません。
・権限クリープ: CI/CDの用途が増えるにつれ、元々S3専用だったキーに権限を追加し続け、気づけば広権限のキーが1本という状態になります。
・監査の困難さ: キーが複数のジョブで共用されていると、CloudTrailのログを見ても「どのジョブが何をしたか」を特定しにくくなります。
OIDCフェデレーションを使えば、これらの問題を根本から解消できます。
OIDCフェデレーションとは何か
OIDC(OpenID Connect)はOAuth 2.0の上に構築された認証プロトコルです。「誰であるか(アイデンティティ)」をJWT形式の「IDトークン」として発行・検証する仕組みです。
フェデレーションとは、複数のシステム間で「このトークンを発行した組織を信頼する」という合意を結び、アイデンティティを相互承認する仕組みです。オンプレ環境で使われるSAML 2.0フェデレーション(Active DirectoryとSalesforceの連携など)と概念は同じですが、OIDCはJSONベースのトークンを使うためシンプルで実装しやすい点が特徴です。
GitHub ActionsのOIDCフェデレーションでは、次の流れで認証が行われます。
・ステップ1: GitHub Actionsのジョブが起動すると、GitHubがそのジョブに固有のIDトークン(JWT)を発行します。トークンにはリポジトリ名・ブランチ名・環境名などのクレームが含まれます。
・ステップ2: ワークフローがIDトークンをAWS STS(Security Token Service)に提示し、一時的な認証情報を要求します。
・ステップ3: AWSはIDトークンの署名をGitHubの公開鍵で検証し、IAMロールの信頼ポリシーと照合します。条件が一致すれば、有効期限付きの一時認証情報を発行します。
・ステップ4: ワークフローはこの一時認証情報を使ってAWS APIを呼び出します。ジョブ終了後、認証情報は自動で失効します。
長期有効なアクセスキーは一切使いません。すべての認証情報はジョブ単位で発行され、自動で失効します。
AWS側の設定手順
設定は「OIDCプロバイダーの登録」と「IAMロールの作成」の2段階です。
1. IDプロバイダー(OIDC Provider)の登録
AWSにGitHubをOIDCプロバイダーとして登録します。AWSマネジメントコンソールでは「IAM → IDプロバイダー → プロバイダーを追加」から設定できます。CLIで行う場合はこちらです。
# GitHub Actions用OIDCプロバイダーを登録(AWS CLI) aws iam create-open-id-connect-provider \ --url https://token.actions.githubusercontent.com \ --client-id-list sts.amazonaws.com \ --thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1
サムプリント(thumbprint)はGitHubのOIDCエンドポイント証明書のフィンガープリントです。AWSは2023年以降、GitHubのサムプリント検証を一部自動化しているため、証明書が更新されても自動でフォールバックする仕組みが入っています。プロバイダーを登録する際はAWSコンソールの「サムプリントを自動的に取得する」オプションも活用できます。
2. IAMロールの信頼ポリシー設定
OIDCプロバイダーを登録したら、GitHub Actionsが引き受けるIAMロールを作成します。「信頼ポリシー(Trust Policy)」の設計が最も重要なポイントです。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com" }, "Action": "sts:AssumeRoleWithWebIdentity", "Condition": { "StringEquals": { "token.actions.githubusercontent.com:aud": "sts.amazonaws.com" }, "StringLike": { "token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:ref:refs/heads/main" } } } ] }
`Principal.Federated` にはIDプロバイダーのARNを指定します。`Condition` の `sub` フィールドが重要で、「どのリポジトリ・どのブランチのジョブだけにロール引き受けを許可するか」を絞り込みます。ここを `*` のままにしておくと、そのリポジトリ内の任意のブランチからロールを引き受けられてしまうため注意が必要です(詳細は後述)。
`123456789012` は自分のAWSアカウントIDに置き換えてください。
3. アクセス権限ポリシーの付与
信頼ポリシーでロールを引き受けられる主体を定義したら、そのロールに必要な権限ポリシーをアタッチします。S3への書き込みだけが必要なジョブなら、対象バケットと必要なアクションだけに絞ります。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::your-deploy-bucket", "arn:aws:s3:::your-deploy-bucket/*" ] } ] }
最小権限の原則に従い、そのジョブに必要なアクションとリソースだけに絞ることが重要です。「とりあえず `*` フルアクセス」はNGです。
GitHub Actions側の設定手順
1. ワークフローファイルの権限設定
GitHub ActionsがOIDCトークンを取得するには、ワークフローに `id-token: write` 権限が必要です。リポジトリのデフォルト設定(read-only)では取得できないため、明示的に指定します。
permissions: id-token: write # OIDCトークン取得に必要 contents: read # リポジトリ読み取り
2. aws-actions/configure-aws-credentials の設定
AWSが提供する公式アクション `aws-actions/configure-aws-credentials` を使うと、OIDCトークンの取得からSTSへの交換まで自動で行ってくれます。
jobs: deploy: runs-on: ubuntu-latest permissions: id-token: write contents: read steps: - uses: actions/checkout@v4 - name: AWSクレデンシャルの設定 uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::123456789012:role/github-actions-deploy-role aws-region: ap-northeast-1 - name: S3にデプロイ run: aws s3 sync ./dist s3://your-deploy-bucket/
`role-to-assume` に先ほど作成したIAMロールのARNを指定するだけです。アクセスキーIDもシークレットアクセスキーもどこにも書きません。`aws-actions/configure-aws-credentials` がOIDCトークンを取得してSTSで交換し、後続ステップで使えるように環境変数へセットしてくれます。
設計上の注意点と実務Tips
【重要】subフィールドの条件式でスコープを絞る
OIDCフェデレーション設計でよくある失敗が「`sub` 条件を緩くしすぎる」ことです。
`sub` フィールドの値は `repo:
# NG: リポジトリ内の全ブランチ・全ジョブからロールを引き受けられる "token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:*" # OK: mainブランチからのジョブのみ許可 "token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:ref:refs/heads/main" # OK: productionというGitHub Environmentを持つジョブのみ許可 "token.actions.githubusercontent.com:sub": "repo:your-org/your-repo:environment:production"
本番デプロイに使うロールは、`main` ブランチまたは本番用GitHub Environmentに絞るべきです。feature ブランチからのジョブが誤って本番ロールを引き受けてしまう事故を防げます。
環境別のロール分離
ひとつのロールをdev/stg/prodで兼用することは避けましょう。環境ごとに別のIAMロールを作り、それぞれのロールに紐づく権限の範囲を分けます。
| 環境 | IAMロール例 | subの条件 | 権限の範囲 |
|---|---|---|---|
| 開発(dev) | github-actions-dev-role | ref:refs/heads/develop | dev用S3・ECSのみ |
| ステージング(stg) | github-actions-stg-role | ref:refs/heads/staging | stg用リソースのみ |
| 本番(prod) | github-actions-prod-role | environment:production | prodに必要なアクションのみ |
マルチアカウント構成でdev・stg・prodをアカウント分けしている場合は、それぞれのアカウントにOIDCプロバイダーを登録してロールを作成します。AWSの環境分離設計(アカウント分割 vs VPC分割)と組み合わせることで、ガードレールの強固なCI/CD基盤を構築できます。
GitHub Environmentの承認ゲートを活用する
`environment:production` を条件にしただけでは、GitHub Environmentの設定で「Required reviewers(必須レビュアー)」を設けていないと保護になりません。本番デプロイのワークフローでは、Environmentの承認ゲートを必ず設定しましょう。承認者がApproveしたジョブのみがproductionロールを引き受けられるようになります。
CloudTrailとの組み合わせで監査精度を上げる
一時認証情報による操作もCloudTrailに記録されます。ロール名にリポジトリ名・環境名を含めておくと(例: `github-actions-my-app-prod-role`)、CloudTrailのログを見たときに「どのリポジトリのどのジョブが何をしたか」を素早く特定できます。CloudTrail・GuardDutyによる監査と脅威検知と組み合わせることで、CI/CDパイプライン由来の操作ログを効果的に追跡できます。
GitLab CI・CircleCI・Azure DevOps への対応
OIDCフェデレーションはGitHub Actionsだけの仕組みではありません。主要なCI/CDプラットフォームが対応しています。
・GitLab CI/CD: GitLab 15.7以降でOIDCトークン発行をサポート。`GITLAB_OIDC_TOKEN` 変数を使い、AWSのOIDCプロバイダーURLには `https://gitlab.com` を指定します(セルフホスト版は自ドメイン)。セルフホストの場合はGitLabのissuerURLが変わるため、IDプロバイダーのURLをそれぞれのインスタンスURLに合わせます。
・CircleCI: `CIRCLE_OIDC_TOKEN` 環境変数でOIDCトークンを取得可能です。OIDCプロバイダーURLは `https://oidc.circleci.com/org/
・Azure DevOps: Azure DevOpsからAWSへのOIDCも同様の仕組みで設定できます。OIDCプロバイダーURLは `https://vstoken.dev.azure.com/
・AWS CodeBuild: CodeBuild自体はIAMサービスロールで動くためアクセスキー不要です。外部CI/CDからCodeBuildを呼び出す場合はOIDC連携が有効です。
いずれのプラットフォームでも「OIDCプロバイダーURLとクライアントID(通常は `sts.amazonaws.com`)をAWSに登録し、信頼ポリシーを設定する」手順は共通です。
よくあるトラブルと対処法
・「Not authorized to perform sts:AssumeRoleWithWebIdentity」エラー: 信頼ポリシーの `sub` 条件がジョブのコンテキストと一致していない場合に発生します。まずCloudTrailで実際にどのような `sub` クレームが送られてきているか確認し、条件式を合わせましょう。STSのCloudTrailイベント(`AssumeRoleWithWebIdentity`)の `requestParameters` に `sub` クレームが記録されています。
・「Failed to retrieve OIDC Token」エラー: ワークフローファイルの `permissions` に `id-token: write` が設定されていない場合に発生します。ジョブレベルまたはワークフロー全体の `permissions` 設定を確認してください。
・サムプリントエラー: GitHubの証明書が更新されてサムプリントが変わった場合に発生することがあります。AWSコンソールのIDプロバイダー設定からサムプリントを更新します。
・ロールARNの管理: IAMロールのARNをワークフローに直書きしてもセキュリティ上の問題はありませんが、環境ごとに変わる場合はGitHub VariablesやOrganizationレベルのVariablesで管理するとメンテナンスしやすくなります。
・既存アクセスキーとの併用移行: 移行期間中は新旧両方を動かしながら段階的に切り替えることも可能です。OIDC方式で正常に動作することを確認した後、旧IAMユーザーを削除します。

本記事のまとめ
| 比較項目 | アクセスキー方式 | OIDCフェデレーション方式 |
|---|---|---|
| 認証情報の種類 | 長期有効なアクセスキー | ジョブ単位の一時認証情報(自動失効) |
| 漏洩リスク | 高い(誤コミット等で発生) | 低い(永続的なシークレットなし) |
| ローテーション | 手動で定期実施が必要 | 不要(自動失効) |
| 監査追跡 | キー共用時に追跡困難 | ロール+ジョブ単位で追跡可能 |
| 設定の複雑さ | 簡単(キー発行して登録するだけ) | 初期設定が必要(OIDC登録 + IAMロール) |
| 主な対応プラットフォーム | ─ | GitHub Actions・GitLab CI・CircleCI |
OIDCフェデレーションの初期設定は少し手間がかかりますが、一度設定すれば「アクセスキーが漏洩したかも」という不安から解放されます。新しいCI/CDパイプラインを構築する際はOIDC方式を標準として設計することをお勧めします。
既存のアクセスキー方式からの移行も、「新ロールを作成 → ワークフローを書き換えてテスト → 旧IAMユーザーを削除」という手順でスムーズに進められます。
クラウドセキュリティの基礎となる認証・認可の概念については認証(Authentication)と認可(Authorization)の違いもあわせてご覧ください。ゼロトラストの観点からCI/CDパイプラインのセキュリティを考えるならゼロトラストセキュリティ入門も参考になります。
PR
AWSではじめるクラウドセキュリティ(松本照吾ほか/日経BP)
IAM設計・VPCセキュリティ・暗号化・コンプライアンスまで、AWS上のセキュリティを体系的に学べる一冊。OIDCフェデレーションを含む認証設計の背景知識を深めたい方に最適です。
