0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

GitHub Actions × AWS OIDC で急に `Not authorized to perform sts:AssumeRoleWithWebIdentity` になった話(原因は Immutable Subject Claim)

0
Posted at

TL;DR

  • 新規作成した GitHub リポジトリから、これまでと同じ手順で AWS OIDC 連携(aws-actions/configure-aws-credentials)を設定したところ、sts:AssumeRoleWithWebIdentityNot authorized で失敗した。
  • 原因は、GitHub が 2026年7月15日以降に作成されたリポジトリに対して、OIDC トークンの sub クレームをデフォルトで Immutable Subject Claim(不変ID付き形式) に切り替えたため。
  • IAM の信頼ポリシーが従来の repo:<org>/<repo>:ref:refs/heads/<branch> という名前ベースの形式のままだったので、実際に発行されるトークンの sub とマッチしなくなっていた。
  • CloudTrail の errorMessage だけでは「なぜ拒否されたか」は分からず、同じイベントの userIdentity.principalId まで見て初めて実際の sub の中身が分かった。

やろうとしていたこと

CDK で構築している AWS 環境に対し、GitHub Actions から CodePipeline を起動するワークフローを新規リポジトリに追加しようとしていました。認証方式は長期のアクセスキーを持たない OIDC(Web ID Federation)方式です。

name: Deploy via CodePipeline

on:
  push:
    branches:
      - main
  workflow_dispatch:

jobs:
  trigger-pipeline:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@v7
      - name: Configure AWS credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v6
        with:
          role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
          role-session-name: github-actions-codepipeline
          aws-region: ap-northeast-1

IAM 側の信頼ポリシーも、これまで他のリポジトリで問題なく使えていた形式で用意していました。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Statement1",
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<ACCOUNT_ID>:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:<org>/<repo>:*"
        }
      }
    }
  ]
}

しかしワークフローを実行すると、Configure AWS credentials (OIDC) のステップで以下のエラーになりました。

Assuming role with OIDC
Retry AssumeRole: attempt 1 of 12 failed: Could not assume role with OIDC: Not authorized to perform sts:AssumeRoleWithWebIdentity. Retrying after 3ms.
...

最初に疑って外れたポイント

エラーメッセージ自体は非常に汎用的で、原因の手がかりになりません。以下を順番に確認していくことにしました。

  1. Secret に設定した Role ARN の値。ワークフローに一時的なデバッグステップを仕込み、アカウントID部分の下6桁だけマスクして出力し、想定のロールと一致することを確認。
- name: Debug Role ARN (temporary)
  run: |
    echo "${{ secrets.AWS_ROLE_ARN }}" | sed 's/\(arn:aws:iam::[0-9]\{6\}\)[0-9]\{6\}/\1XXXXXX/'
  1. リポジトリ名・ブランチ名の一致sub 条件のリポジトリ名部分と、実行しているリポジトリのオーナー名・リポジトリ名(大文字小文字含む)を突き合わせ、一致を確認。
  2. OIDC プロバイダーの重複登録。IAM の Identity providers 一覧を確認し、token.actions.githubusercontent.com が1つだけであることを確認。

ここまで全て「問題なし」でした。

決め手になった CloudTrail の確認

AssumeRoleWithWebIdentity の失敗は AWS 側で AccessDenied として記録されるため、CloudTrail の Event history で該当イベントを確認しました。

  • Event name: AssumeRoleWithWebIdentity
  • 対象リージョン: ワークフローで指定したリージョン

errorMessage はワークフローのログと同じ Not authorized to perform sts:AssumeRoleWithWebIdentity のままで、それだけでは解決できませんでした。
他の情報も見てみると、userIdentity で原因に気づけました。

"userIdentity": {
    "type": "WebIdentityUser",
    "principalId": "arn:aws:iam::<ACCOUNT_ID>:oidc-provider/token.actions.githubusercontent.com:sts.amazonaws.com:repo:<org>@12345678/<repo>@1234567890:ref:refs/heads/main",
    "userName": "repo:<org>@12345678/<repo>@1234567890:ref:refs/heads/main",
    ...
}

userName に GitHub から発行されていた sub が記録されており、原因が判明しました。

repo:<org>@12345678/<repo>@1234567890:ref:refs/heads/main

信頼ポリシーで想定していたのは repo:<org>/<repo>:* という名前だけの形式です。しかし実際の sub はオーナー名とリポジトリ名それぞれに @<数値ID> が挿入された形式 になっていました。StringLike のワイルドカードは末尾にしか付けていなかったため、文字列の途中に挿入された @12345678@1234567890 を吸収できません。これが条件不一致を招き、AccessDenied になっていました。

原因は GitHub の Immutable Subject Claim 移行

GitHub は、OIDC トークンの sub クレームの仕様を段階的に変更しています。従来は repo:<org>/<repo>:ref:refs/heads/<branch> という名前ベースの形式(mutable)でした。新しい immutable 形式では repo:<org>@<OWNER_ID>/<repo>@<REPO_ID>:ref:refs/heads/<branch> のように、オーナー名とリポジトリ名の直後に不変の数値 ID(@<OWNER_ID>@<REPO_ID>)が挿入されます。

新形式は、リポジトリ名や Organization 名が削除・再利用された際に、古い信頼ポリシーが同名の別リポジトリに誤ってマッチしてしまうリスクを防ぐためのものです。2026年7月15日以降に作成されたリポジトリはこの新形式がデフォルトになり、それより前に作られたリポジトリでもオプトインで有効化できます。

今回ハマったリポジトリは新規作成したものだったため、意識せずこの新形式の対象になっていました。既存の古いリポジトリでは同じワークフロー・同じ信頼ポリシーの書き方で問題なく動いていたため、「他のリポジトリでは動いているのに、このリポジトリだけ失敗する」という状況になっていました。

対処方法

信頼ポリシーの sub 条件を、実際に発行される値に合わせて修正します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "Statement1",
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::<ACCOUNT_ID>:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:<org>@12345678/<repo>@1234567890:ref:refs/heads/main"
        }
      }
    }
  ]
}

<OWNER_ID><REPO_ID> は、対象リポジトリの SettingsActionsGeneral の OIDC 関連設定ページから確認できます(実際に発行される sub の形式がそのまま表示されます)。CloudTrail から逆算する方法でも確認できますが、事前にワークフローを1回実行して失敗させる必要がある点は注意してください。

なお <REPO_ID> はリポジトリ名を変更しても不変ですが、ref:refs/heads/main の部分はブランチ運用に応じて調整が必要です。develop/staging/main のようなブランチ戦略を運用している場合は、対象ブランチごとに条件を用意するか、影響範囲を理解した上でワイルドカードを検討してください。

新形式への移行自体を避けたい場合は、リポジトリ側の設定でレガシー形式(名前ベース)のままにするオプトアウトも可能です。ただし新形式はリポジトリ名や Organization 名の再利用による誤マッチを防ぐための仕組みなので、特別な理由がなければ、オプトアウトせず新形式に合わせて信頼ポリシーを直す方をおすすめします。

切り分けの勘所

  • Not authorized to perform sts:AssumeRoleWithWebIdentity というエラーメッセージ自体には、拒否理由の情報がほとんど含まれない。
  • 信頼ポリシーの Principal / Condition / Role ARN / リポジトリ名を一通り確認しても解決しない場合、CloudTrail の AssumeRoleWithWebIdentity イベントの userIdentity.principalId を見て、実際に発行された sub の値を確認するのが最も確実
  • 2026年7月15日以降に作成した(またはオプトインした)リポジトリでは、sub の形式が repo:<org>/<repo>:... から repo:<org>@<OWNER_ID>/<repo>@<REPO_ID>:... に変わっている可能性を疑う。

参考リンク

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?