テックブログ

GitHub Actions OIDCでAWSへ鍵なしデプロイ:AssumeRoleを最小権限で

GitHub Actions OIDCでAWSへ鍵なしデプロイ:AssumeRoleを最小権限で

OIDCを使ったGitHub ActionsのAWS連携は、長期間有効なAccess KeyをSecretsへ置かずに、実行中のワークフローだけが短時間使える認証情報を受け取る仕組みです。GitHub Actionsが発行するIDトークンをAWSが検証し、条件に合うときだけ一時的にRoleを引き受けるのがAssumeRoleWithWebIdentityの考え方です。つまり、あらかじめ固定鍵を配るのではなく、「このリポジトリのこのワークフローなら、今この瞬間だけ使ってよい」と信頼関係で認証する方式なので、漏えいリスクを下げつつ、最小権限の運用に寄せやすくなります。

関連: GitHub Actionsのシークレット漏えい対策:禁止パターンと代替手段

1. OIDCとは(CIでの意味)

1-1. 長期鍵の問題点

GitHub ActionsからAWSへデプロイするとき、昔ながらのやり方ではAWSのAccess KeyとSecret Access KeyをGitHub Secretsへ置くことが多くありました。ですが、この方法は分かりやすい反面、長期鍵をCIへ預けること自体がリスクになります。

長期鍵は、一度漏れると有効期限が長く、攻撃者に長く使われやすいのが問題です。しかも、どのworkflowでも同じ鍵を使い回していると、「本当はstagingだけでよい権限」が本番操作にも使える状態になりやすいです。さらに、ローテーション忘れ、権限過大、誰がどこで使っているか分かりにくい、といった運用上のつらさも出ます。

特に1〜2年目のうちは、「Secretsに入れているから安全」と思いやすいですが、Secretsは漏えいリスクをゼロにする仕組みではありません。長期鍵を保管する構造そのものがリスクなので、そもそも固定鍵を置かない方向へ寄せるのがOIDCの大きな価値です。

1-2. OIDCで短命トークンにする仕組み

OIDCを使うと、GitHub Actionsが実行中に発行するIDトークンを使って、AWSから一時的な認証情報を受け取れます。これが「鍵なしデプロイ」と言われる理由です。

流れとしては、GitHub ActionsがOIDCトークンを発行し、そのトークンをAWS Security Token Serviceが検証します。そして、あらかじめ設定した条件に一致していれば、AWSは短時間だけ使える一時クレデンシャルを返します。このとき使うのがAssumeRoleWithWebIdentityで、「Web Identityを使ってRoleを引き受ける」という意味です。

重要なのは、AWS鍵を事前配布しないことと、使える時間が短いことです。仮に一時クレデンシャルが露出しても、長期鍵より被害を小さくしやすくなります。OIDCは便利な近道というより、「固定秘密を持ち回らない設計」へ変えるための仕組みだと理解すると実務で使いやすいです。

2. AWS側の設定(信頼ポリシー)

2-1. OIDC ProviderとRole

AWS側で最初に必要なのは、GitHub Actionsを信頼するためのOIDC Providerと、引き受け対象のIAM Roleを作ることです。ここでAWSは「誰のトークンを信じるか」を決めます。

OIDC Providerは、GitHubのトークン発行元をAWSへ登録する設定です。その上で、「このProviderから来たトークンなら、このRoleを引き受けてよい」という信頼関係をRole側へ設定します。Roleにはさらに、実際に許可したいAWS操作の権限ポリシーを付けます。つまり、Providerは信頼元、Roleは実際に借りる権限の入れ物、という役割分担です。

ここでよくある誤解は、「OIDC Providerを作れば終わり」と考えることです。実際には、そのProviderを使ってどのRoleを、どんな条件で引き受けられるかまで絞らないと安全になりません。OIDC導入では、ProviderよりもRoleの信頼条件と権限の絞り方のほうが重要です。

2-2. trust policyで縛るポイント(sub/aud)

OIDC連携で一番大事なのは、trust policyで引き受け条件をきちんと絞ることです。特にsubaudは必ず確認したいポイントです。

aud は、このトークンが誰向けに発行されたかを表す値で、AWS向けなら通常は sts.amazonaws.com を使います。sub は、どのリポジトリ・どのブランチ・どの環境から来たワークフローかを示す識別子です。ここを広く許しすぎると、想定していないブランチやworkflowからもRoleを引き受けられる可能性があります。

{
  "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",
          "token.actions.githubusercontent.com:sub": "repo:example-org/example-repo:ref:refs/heads/main"
        }
      }
    }
  ]
}

この例では、特定リポジトリのmainブランチからの実行だけを許可しています。注意点は、最初からワイルドカードで広く開けないことです。たとえば repo:example-org/* のように広くすると、想定外のリポジトリまで信頼してしまう可能性があります。まずは狭く始めて、必要な範囲だけ広げるほうが安全です。

3. GitHub Actions側の設定

3-1. permissionsの必須(id-token)

GitHub Actions側では、OIDCトークンを受け取るために id-token: write が必須です。これがないと、AWSへ渡すIDトークン自体を取得できません。

OIDCでは、GitHub Actionsがその場で署名付きトークンを発行します。そのため、workflowやjobにOIDCトークン発行の権限を与える必要があります。加えて、リポジトリを読むための contents: read など、必要最小限の権限も合わせて指定するのが基本です。ここでも大事なのは、便利だから広くするのではなく、必要なものだけを明示することです。

permissions:
  id-token: write
  contents: read

この設定は小さいですが、OIDC連携ではかなり重要です。注意点は、workflow全体に広い権限を与えたままにしないことです。OIDC用に必要な id-token: write は付けつつ、それ以外はできるだけ狭くする、という考え方を崩さないほうが安全です。

3-2. 実行例(最小のworkflow)

GitHub Actions側の最小例では、AWSのRoleをOIDC経由で引き受け、その一時クレデンシャルでAWS CLIを動かす形になります。実務では公式アクションを使うと分かりやすいです。

流れは、コードをチェックアウトし、OIDC対応のAWS認証アクションでRoleを引き受け、その後にS3やECR、ECSなどのデプロイ処理を実行するだけです。従来のように AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY をSecretsへ置く必要がなくなるのが大きな違いです。つまり、認証の入り口が「固定鍵」から「実行時の信頼関係」へ変わります。

name: deploy

on:
  push:
    branches:
      - main

permissions:
  id-token: write
  contents: read

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Configure AWS credentials
        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: Verify identity
        run: aws sts get-caller-identity

      - name: Deploy
        run: |
          aws s3 cp dist/ s3://example-bucket/ --recursive

このworkflowでは、mainブランチへのpush時だけRoleを引き受けてデプロイしています。注意点は、まず aws sts get-caller-identity で本当に想定Roleになっているか確認することです。最初から複雑なデプロイ処理を入れるより、認証が通ることを確認してから本処理を足すほうが詰まりにくいです。

4. 最小権限の考え方

4-1. Role権限を絞る

OIDCを使っても、引き受けるRoleの権限が広すぎると安全にはなりません。鍵なしになっても、Role自体が強すぎれば漏えい時の被害は大きいままです。

たとえば、S3への静的ファイル配布だけが目的なら、そのRoleには対象バケットへの必要操作だけを与えるべきです。ECRへpushするだけなら、ECR関連の権限に絞るべきです。よくないのは、「とりあえず管理者権限で動かす」ことです。OIDCは認証方法の改善であって、認可を雑にしてよい理由にはなりません。

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

この例では、特定のS3バケットだけへ必要操作を許可しています。注意点は、最初の動作確認で広い権限を付け、そのまま本番まで残してしまうことです。まず動かすための一時的な広さと、本番で使う最小権限は分けて考え、最後に必ず絞ることが大切です。

4-2. 環境別(stg/prod)の分離

本番とステージングを同じRoleで扱わず、環境ごとにRoleや信頼条件を分けると安全性が上がります。これは権限事故と運用ミスの両方を減らすためです。

たとえば、stgはdevelopブランチから、prodはmainブランチか承認付きworkflowからだけ引き受けられるようにします。さらに、Role自体も環境ごとに分けておけば、stg用の権限が本番へ届くことを防ぎやすくなります。これは「認証を分ける」というより、「環境ごとの責任範囲を分ける」考え方です。

実務では、「stg-role」「prod-role」を分けるだけでもかなり効果があります。加えて、GitHub Environmentsの承認ゲートと組み合わせると、本番だけ人の確認を挟む運用も作りやすいです。環境分離は面倒に見えますが、後から事故を追いやすくなるという意味でも価値があります。

5. まとめ:設定チェックリスト

GitHub ActionsのOIDCでAWSへ鍵なしデプロイするポイントは、長期鍵をSecretsへ置かず、GitHubが発行する短命トークンを使って、その場限りでRoleを引き受けることです。これにより、固定鍵の漏えいリスクを下げつつ、どのリポジトリ・どのブランチ・どの環境から使えるかを信頼ポリシーで絞りやすくなります。

  • AWS側に GitHub Actions 用の OIDC Provider を作成しているか
  • IAM Role の trust policy で audsts.amazonaws.com に絞っているか
  • sub でリポジトリやブランチを必要最小限に絞っているか
  • GitHub Actions 側で permissions: id-token: write を付けているか
  • workflowに固定のAWS鍵を置いていないか
  • Role権限をデプロイ先や操作内容に合わせて最小化しているか
  • stg/prod で Role や条件を分離しているか
  • 最初に aws sts get-caller-identity で想定Roleを確認しているか

OIDCは難しそうに見えますが、やっていることは「固定秘密を配らず、信頼関係で一時権限を渡す」だけです。最初から完璧な多環境構成を作る必要はなくても、少なくとも trust policy を狭くし、Role権限を小さくし、固定鍵を置かない、という3点は早めに押さえる価値があります。

6. 参考リンク