mcpField
G
github-actions-oidc-aws

github-actions-oidc-aws

github.com/loxosceles-devクラウド・DevOps136 回インストール
リポジトリ

github-actions-oidc-aws について

GitHub Actions から AWS へアクセスする際、長期 IAM 認証情報の代わりに OIDC でロールを引き受ける方法を示すリファレンススキルです。チーム向けに、設定、リポジトリ制限、権限、デプロイ方式、トラブルシューティングを扱います。

主な機能

  1. 01OIDC による一時 AWS 認証情報
  2. 02リポジトリとブランチの信頼ポリシー制限
  3. 03最小権限に基づく IAM 設定
  4. 04CDK、Terraform、CLI、CloudFormation の設定例
  5. 05デプロイ方式とトラブルシューティング

活用シーン

  1. 01デプロイ前の GitHub Actions の AWS 認証
  2. 02AWS ロールの対象を特定リポジトリやブランチに限定
  3. 03保存済み AWS アクセスキーの置き換え
  4. 04OIDC エラーやロール権限の調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/loxosceles-dev/dev-skills --skill github-actions-oidc-aws

インストール後、Claude Code で/github-actions-oidc-awsを入力して読み込まれたか確認します。

詳細

AWS 向け GitHub Actions OIDC 認証

これは参考パターンです。 このアプローチを学び、状況に合わせて応用してください。そのままコピーしないでください。

ステータス:🔴 重大なパターン
カテゴリ:CI/CD/インフラストラクチャ
適用対象:GitHub Actions を使用して AWS にデプロイするすべてのプロジェクト


概要

長期間有効な IAM 認証情報の代わりに、OpenID Connect(OIDC)を使用して AWS リソースにアクセスする、GitHub Actions ワークフロー向けの安全な認証パターンです。GitHub シークレットに AWS アクセスキーを保存する必要がなくなります。

主なメリット:

  • ローテーションや漏えいの心配がある長期間有効な認証情報が不要
  • 有効期限が自動設定される一時的な認証情報
  • リポジトリを対象としたアクセス制御
  • AWS CloudTrail による監査証跡
  • 業界のベストプラクティス(AWS と GitHub が推奨)

問題

GitHub Actions ワークフローは、インフラストラクチャのデプロイ、パイプラインのトリガー、またはリソースの管理を行うために、AWS に対して認証する必要があります。従来の方法には、セキュリティ上の問題があります。

アンチパターン:静的 IAM 認証情報

# ❌ Security risk: long-lived credentials in secrets
- uses: aws-actions/configure-aws-credentials@v4
  with:
    aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
    aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

問題点:

  • 認証情報に有効期限がありません(手動でローテーションする必要があります)
  • 漏えいした場合、攻撃者が継続的にアクセスできます
  • 特定のリポジトリにアクセスを制限する方法がありません
  • どのワークフローが認証情報を使用したかを監査するのが困難です
  • 最小権限の原則に違反します

解決策

GitHub の OIDC プロバイダーを使用し、AWS IAM ロールの引き受けを通じて一時的な認証情報を発行します。

フロー:

GitHub Actions → OIDC Token → AWS STS → Temporary Credentials → AWS Resources

仕組み:

  1. GitHub Actions が GitHub に OIDC トークンをリクエストします
  2. ワークフローがトークンを AWS STS に提示します
  3. AWS が IAM OIDC プロバイダーに照らしてトークンを検証します
  4. AWS が一時的な認証情報を発行します(有効期間は 1 時間)
  5. ワークフローが一時的な認証情報を使用して AWS にアクセスします

コンポーネント

1. AWS IAM OIDC プロバイダー

AWS と GitHub の OIDC 発行者間の信頼関係を確立します。

設定:

  • URL:https://token.actions.githubusercontent.com
  • オーディエンス:sts.amazonaws.com
  • サムプリント:1c58a3a8518e8759bf075b76b750d4f2df264fcd(GitHub ルート CA)

ルート CA のサムプリントを使う理由

  • 中間証明書より安定しています
  • GitHub が中間証明書をローテーションしても、信頼関係が損なわれません
  • AWS のドキュメントで推奨されています
2. 信頼ポリシーを設定した IAM ロール

信頼ポリシーでアクセスを制限し、GitHub Actions が引き受けられるロールです。

信頼ポリシーの構造:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::{account}: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:{owner}/{repo}:*"
      }
    }
  }]
}

信頼ポリシーのスコープ設定オプション:

# All branches and tags
"repo:owner/repo:*"

# Specific branch only
"repo:owner/repo:ref:refs/heads/main"

# Multiple branches
["repo:owner/repo:ref:refs/heads/main", "repo:owner/repo:ref:refs/heads/dev"]

# Pull requests
"repo:owner/repo:pull_request"

# Environment-specific
"repo:owner/repo:environment:production"
3. IAM アクセス許可ポリシー

ロールが AWS で実行できる操作を定義します(最小権限の原則)。

ユースケース別の例:

パイプラインのトリガーのみ:

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "codepipeline:StartPipelineExecution",
    "Resource": "arn:aws:codepipeline:*:{account}:pipeline-name-*"
  }]
}

S3 への直接デプロイ:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:DeleteObject", "s3:ListBucket"],
      "Resource": ["arn:aws:s3:::bucket-name", "arn:aws:s3:::bucket-name/*"]
    },
    {
      "Effect": "Allow",
      "Action": "cloudfront:CreateInvalidation",
      "Resource": "*"
    }
  ]
}

インフラストラクチャのデプロイ(慎重に使用してください):

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "*",
    "Resource": "*"
  }]
}
4. GitHub Actions ワークフローの設定

ワークフローでは、OIDC トークンをリクエストしてロールを引き受ける必要があります。

必要なアクセス許可:

permissions:
  id-token: write  # Required for OIDC token
  contents: read   # Required to checkout code

認証ステップ:

- uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::{account}:role/{role-name}
    aws-region: {region}

実装

ステップ 1:OIDC プロバイダーを作成する(一度だけの設定)

インフラストラクチャツールを選択してください。

AWS CDK(TypeScript):

import * as iam from 'aws-cdk-lib/aws-iam';

const provider = new iam.OpenIdConnectProvider(this, 'GitHubProvider', {
  url: 'https://token.actions.githubusercontent.com',
  clientIds: ['sts.amazonaws.com'],
  thumbprints: ['1c58a3a8518e8759bf075b76b750d4f2df264fcd']
});

Terraform:

resource "aws_iam_openid_connect_provider" "github" {
  url = "https://token.actions.githubusercontent.com"
  client_id_list = ["sts.amazonaws.com"]
  thumbprint_list = ["1c58a3a8518e8759bf075b76b750d4f2df264fcd"]
}

AWS CLI:

aws iam create-open-id-connect-provider \
  --url https://token.actions.githubusercontent.com \
  --client-id-list sts.amazonaws.com \
  --thumbprint-list 1c58a3a8518e8759bf075b76b750d4f2df264fcd

CloudFormation:

GitHubOIDCProvider:
  Type: AWS::IAM::OIDCProvider
  Properties:
    Url: https://token.actions.githubusercontent.com
    ClientIdList:
      - sts.amazonaws.com
    ThumbprintList:
      - 1c58a3a8518e8759bf075b76b750d4f2df264fcd
ステップ 2:IAM ロールを作成する

AWS CDK(TypeScript):

const role = new iam.Role(this, 'GitHubActionsRole', {
  roleName: 'GitHubActionsRole',
  assumedBy: new iam.WebIdentityPrincipal(provider.openIdConnectProviderArn, {
    StringEquals: {
      'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com'
    },
    StringLike: {
      'token.actions.githubusercontent.com:sub': `repo:${owner}/${repo}:*`
    }
  }),
  inlinePolicies: {
    DeploymentPermissions: new iam.PolicyDocument({
      statements: [
        new iam.PolicyStatement({
          effect: iam.Effect.ALLOW,
          actions: ['codepipeline:StartPipelineExecution'],
          resources: [`arn:aws:codepipeline:*:${this.account}:pipeline-*`]
        })
      ]
    })
  }
});

Terraform:

resource "aws_iam_role" "github_actions" {
  name = "GitHubActionsRole"
  
  assume_role_policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Principal = {
        Federated = aws_iam_openid_connect_provider.github.arn
      }
      Action = "sts:AssumeRoleWithWebIdentity"
      Condition = {
        StringEquals = {
          "token.actions.githubusercontent.com:aud" = "sts.amazonaws.com"
        }
        StringLike = {
          "token.actions.githubusercontent.com:sub" = "repo:${var.github_owner}/${var.github_repo}:*"
        }
      }
    }]
  })
}

resource "aws_iam_role_policy" "github_actions" {
  name = "deployment-permissions"
  role = aws_iam_role.github_actions.id
  
  policy = jsonencode({
    Version = "2012-10-17"
    Statement = [{
      Effect = "Allow"
      Action = "codepipeline:StartPipelineExecution"
      Resource = "arn:aws:codepipeline:*:${data.aws_caller_identity.current.account_id}:pipeline-*"
    }]
  })
}
ステップ 3:GitHub Actions ワークフローを更新する

変更前(静的認証情報):

name: Deploy
on: [push]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: us-east-1
      
      - run: aws s3 sync ./dist s3://my-bucket

変更後(OIDC):

name: Deploy
on: [push]

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
          aws-region: us-east-1
      
      - run: aws s3 sync ./dist s3://my-bucket
ステップ 4:古いシークレットを削除する(クリーンアップ)
# List current secrets
gh secret list

# Remove old credentials
gh secret remove AWS_ACCESS_KEY_ID
gh secret remove AWS_SECRET_ACCESS_KEY

設定管理

アカウント ID のハードコーディングを避ける

アンチパターン:

role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole

改善策:リポジトリ変数を使用する:

role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsRole

最善策:設定ファイルから読み取る:

- name: Load config
  id: config
  run: |
    echo "account=$(grep '^AWS_ACCOUNT_ID=' .env | cut -d'=' -f2)" >> $GITHUB_OUTPUT
    echo "region=$(grep '^AWS_REGION=' .env | cut -d'=' -f2)" >> $GITHUB_OUTPUT

- uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::${{ steps.config.outputs.account }}:role/GitHubActionsRole
    aws-region: ${{ steps.config.outputs.region }}

デプロイパターン

パターン A:パイプラインのトリガー

GitHub Actions が AWS CodePipeline をトリガーし、実際のデプロイを処理します。

次の場合に使用します:

  • 複雑な複数ステージのデプロイ
  • AWS ネイティブのデプロイ履歴が必要な場合
  • 複数のソースからトリガーしたい場合
  • ビルドに大規模なコンピューティングリソースが必要な場合

ワークフロー:

permissions:
  id-token: write
  contents: read

jobs:
  trigger:
    runs-on: ubuntu-latest
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsRole
          aws-region: ${{ vars.AWS_REGION }}
      
      - run: aws codepipeline start-pipeline-execution --name my-pipeline

IAM アクセス許可:

{
  "Effect": "Allow",
  "Action": "codepipeline:StartPipelineExecution",
  "Resource": "arn:aws:codepipeline:*:*:pipeline-name"
}
パターン B:直接デプロイ

GitHub Actions がデプロイ全体(ビルド+デプロイ)を実行します。

使用する場合:

  • シンプルな静的サイトのデプロイ
  • 素早いフィードバックループを求める場合
  • GitHub Actions のネイティブ機能を優先する場合
  • コストを重視する場合(CodePipeline/CodeBuild のコストを避ける)

ワークフロー:

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      
      - run: npm ci && npm run build
      
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsRole
          aws-region: ${{ vars.AWS_REGION }}
      
      - run: |
          aws s3 sync dist/ s3://my-bucket/ --delete
          aws cloudfront create-invalidation --distribution-id ${{ vars.CF_DIST_ID }} --paths "/*"

IAM 権限:

{
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["s3:PutObject", "s3:DeleteObject", "s3:ListBucket"],
      "Resource": ["arn:aws:s3:::bucket/*", "arn:aws:s3:::bucket"]
    },
    {
      "Effect": "Allow",
      "Action": "cloudfront:CreateInvalidation",
      "Resource": "*"
    }
  ]
}
パターン C:インフラストラクチャのデプロイ

GitHub Actions がインフラストラクチャの変更(CDK、Terraform、CloudFormation)をデプロイします。

使用する場合:

  • Infrastructure as Code のワークフロー
  • PR ベースでインフラストラクチャをレビューしたい場合
  • マージ前に変更を検証する必要がある場合

ワークフロー:

permissions:
  id-token: write
  contents: read
  pull-requests: write  # For PR comments

jobs:
  plan:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsTerraformRole
          aws-region: ${{ vars.AWS_REGION }}
      
      - run: terraform plan -out=plan.tfplan
      
      - uses: actions/github-script@v7
        with:
          script: |
            // Post plan to PR comment
  
  apply:
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsTerraformRole
          aws-region: ${{ vars.AWS_REGION }}
      
      - run: terraform apply -auto-approve

IAM 権限:通常、広範な権限(AdministratorAccess または PowerUserAccess)が必要です。plan(読み取り専用)と apply(書き込み)で別々のロールを使用することを検討してください。


複数環境の戦略

環境ごとに個別のロールを設定する

推奨:開発/ステージング/本番環境ごとに、異なる権限を持つ個別のロールを作成してください。

CDK の例:

['dev', 'prod'].forEach(env => {
  new iam.Role(this, `GitHubActionsRole-${env}`, {
    roleName: `GitHubActionsRole-${env}`,
    assumedBy: new iam.WebIdentityPrincipal(provider.openIdConnectProviderArn, {
      StringEquals: {
        'token.actions.githubusercontent.com:aud': 'sts.amazonaws.com'
      },
      StringLike: {
        'token.actions.githubusercontent.com:sub': 
          env === 'prod' 
            ? `repo:${owner}/${repo}:ref:refs/heads/main`
            : `repo:${owner}/${repo}:ref:refs/heads/dev`
      }
    })
  });
});

ワークフロー:

- name: Determine environment
  id: env
  run: |
    if [ "${{ github.ref }}" = "refs/heads/main" ]; then
      echo "name=prod" >> $GITHUB_OUTPUT
    else
      echo "name=dev" >> $GITHUB_OUTPUT
    fi

- uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::${{ vars.AWS_ACCOUNT_ID }}:role/GitHubActionsRole-${{ steps.env.outputs.name }}
    aws-region: ${{ vars.AWS_REGION }}

セキュリティに関する考慮事項

1. 最小権限の原則

必要最小限の権限を必ず付与してください:

// ❌ Too broad
{
  "Effect": "Allow",
  "Action": "*",
  "Resource": "*"
}

// ✅ Specific
{
  "Effect": "Allow",
  "Action": "codepipeline:StartPipelineExecution",
  "Resource": "arn:aws:codepipeline:us-east-1:123456789012:my-pipeline"
}
2. リポジトリのスコープ設定

必ず特定のリポジトリに制限してください:

// ❌ Any repository in organization
"token.actions.githubusercontent.com:sub": "repo:my-org/*"

// ✅ Specific repository
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:*"

// ✅ Even more specific (main branch only)
"token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:ref:refs/heads/main"
3. オーディエンスの検証

必ずオーディエンスを検証してください:

{
  "StringEquals": {
    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
  }
}
4. セッション期間

デフォルト:1 時間(ほとんどのワークフローに十分です)

カスタム期間(必要な場合):

- uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
    role-duration-seconds: 3600  # 1 hour (default)
    aws-region: us-east-1
5. CloudTrail による監査

ロールの引き受けを監視します:

aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity \
  --max-results 10

監視する主なフィールド:

  • userIdentity.principalId:GitHub リポジトリとワークフロー
  • requestParameters.roleArn:引き受けられたロール
  • sourceIPAddress:GitHub Actions の IP 範囲
  • userAgent:GitHub Actions のユーザーエージェント

トラブルシューティング

エラー:「Not authorized to perform sts:AssumeRoleWithWebIdentity」

原因:信頼ポリシーがワークフローのコンテキストと一致していません。

確認項目:

  1. リポジトリ名が信頼ポリシーと一致している
  2. ブランチ/タグが信頼ポリシーの条件と一致している
  3. ワークフローに id-token: write 権限がある

デバッグ:

- name: Debug OIDC token
  run: |
    curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
      "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=sts.amazonaws.com" | jq
エラー:「OpenIDConnect provider not found」

原因:OIDC プロバイダーが作成されていないか、ARN が間違っています。

修正方法:

# List providers
aws iam list-open-id-connect-providers

# Check provider details
aws iam get-open-id-connect-provider \
  --open-id-connect-provider-arn arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com
認証成功後に「Access Denied」エラーが発生する

原因:ロールに必要な権限がありません。

修正方法:ロールの権限ポリシーを更新し、必要なアクションを含めてください。

ワークフローが OIDC トークンを要求しない

原因:id-token: write 権限がありません。

修正方法:

permissions:
  id-token: write  # Add this
  contents: read

移行チェックリスト

静的認証情報から OIDC への移行:

  • AWS アカウントに OIDC プロバイダーを作成する
  • 信頼ポリシーを持つ IAM ロールを作成する
  • ロールに権限ポリシーをアタッチする
  • ロールの引き受けを手動でテストする(任意)
  • OIDC を使用するようワークフローを更新する
  • ワークフローに permissions ブロックを追加する
  • 認証情報のシークレットをロール ARN に置き換える
  • 本番環境以外でワークフローをテストする
  • CloudTrail ログにロールの引き受けが記録されていることを確認する
  • 本番環境にデプロイする
  • GitHub から古い AWS 認証情報のシークレットを削除する
  • 古い IAM ユーザーを無効化/削除する(該当する場合)
  • ロール ARN と権限を文書化する

参考資料


段階的な改善

開発者が、このスキルで防ぐべきだった動作を修正した場合は、同じ修正が今後発生しないよう、このスキルに加える具体的な変更を提案してください。

コメント