Claude CodeのBedrock OIDC認証をGitLab CI/CDで組む — GCPのWIFも
Claude CodeをGitLab CI/CDからBedrockやGoogle Cloudで動かすOIDC/WIF設定を、IAMロール・トラストポリシー・ジョブYAMLまで実装単位で確認します。
Claude Code GitLab BedrockのOIDC認証とはどんな仕組みか
Claude CodeのGitLab CI/CD統合はbeta機能で、GitLabが保守しています。サポートの窓口もGitLab側のissueです。認証まわりの挙動は今後変わる可能性があります。
Claude Code GitLab BedrockのOIDC認証とは、GitLabのCI/CDジョブが発行する短命なOIDCトークンをAWS IAMロールの一時認証情報に交換する仕組みです。ANTHROPIC_API_KEYのような静的なキーを保存せずに、Amazon Bedrock経由でClaude Codeを動かせます。Google Cloud側で同じ役割を担うのがWorkload Identity Federation(WIF)です。サービスアカウントキーをダウンロードせずに、ジョブがサービスアカウントを借用します。
認証プロバイダーは3つから選べます。Claude API(SaaSのAPIキー)、Amazon Bedrock(IAMベース)、Google CloudのAgent Platform(GCPネイティブ)です。APIキーだけで足りるならそれが最短です。AWSやGCPの既存契約でデータレジデンシーや調達の要件を満たしたいときに、後の2つが候補になります。ジョブ本体の作り方や@claudeメンションのWebhook設定はClaude CodeをGitLab CI/CDに組み込むにあります。ここでは認証の部分だけを扱います。
ジョブの中でトークンが認証情報に変わるまで
どちらのクラウドでも、流れは共通です。GitLabが発行したトークンをファイルに置き、クラウド側のSTSに渡して一時認証情報を受け取ります。
OIDCトークンが認証情報に変わる4段階
- 1
GitLabがトークンを発行する
ジョブの
id_tokens:ブロックで宣言した変数(例ではGITLAB_OIDC_TOKEN)にJWTが入ります。有効期限は、ジョブのtimeoutを指定していればその値、なければ5分です。 - 2
トークンをファイルに書き出す
AWSは
AWS_WEB_IDENTITY_TOKEN_FILE、GCPは認証設定ファイルのcredential_sourceが、このファイルを指します。 - 3
クラウド側のSTSで交換する
AWSは
sts assume-role-with-web-identityでIAMロールを引き受け、GCPはWorkload Identity Poolを経由してサービスアカウントを借用します。 - 4
claudeが一時認証情報で呼び出す
Bedrockなら
CLAUDE_CODE_USE_BEDROCK=1、Agent PlatformならCLAUDE_CODE_USE_VERTEX=1を立てます。ClaudeはこのフラグでAPIの送り先を切り替えます。
トークンの有効期限は、交換までの猶予です。交換が済んだ後の寿命は、AWSなら--duration-secondsとロールの設定で決まります。
AWS BedrockでOIDC認証を組む手順
AWS側の下準備は4つです。
- Amazon Bedrockで対象のClaudeモデルへのアクセスを有効化する
- GitLabをAWS IAMのOIDCアイデンティティプロバイダーとして登録する
- GitLabプロジェクト・保護されたref(ブランチやタグ)に信頼を絞ったIAMロールを作成する
- Bedrock呼び出しAPIに絞った最小権限のポリシーをそのロールへアタッチする
GitLab側で必要なCI/CD変数は2つだけです。AWS_ROLE_TO_ASSUME(引き受けるIAMロールのARN)とAWS_REGION(Bedrockのリージョン、例: us-west-2)です。audには、AWS側のIAM OIDCプロバイダーに設定した値を指定します。たとえば自社のGitLabインスタンスURLです。
claude-bedrock:
stage: ai
image: node:24-alpine3.21
id_tokens:
GITLAB_OIDC_TOKEN:
aud: https://gitlab.example.com
before_script:
- apk add --no-cache bash curl jq git aws-cli
- curl -fsSL https://claude.ai/install.sh | bash
- export PATH="$HOME/.local/bin:$PATH"
- export AWS_WEB_IDENTITY_TOKEN_FILE="/tmp/oidc_token"
- printf "%s" "$GITLAB_OIDC_TOKEN" > "$AWS_WEB_IDENTITY_TOKEN_FILE"
- >
aws sts assume-role-with-web-identity
--role-arn "$AWS_ROLE_TO_ASSUME"
--role-session-name "gitlab-claude-$(date +%s)"
--web-identity-token "file://$AWS_WEB_IDENTITY_TOKEN_FILE"
--duration-seconds 3600 > /tmp/aws_creds.json
- export AWS_ACCESS_KEY_ID="$(jq -r .Credentials.AccessKeyId /tmp/aws_creds.json)"
- export AWS_SECRET_ACCESS_KEY="$(jq -r .Credentials.SecretAccessKey /tmp/aws_creds.json)"
- export AWS_SESSION_TOKEN="$(jq -r .Credentials.SessionToken /tmp/aws_creds.json)"
script:
- /bin/gitlab-mcp-server || true
- claude -p "$AI_FLOW_INPUT" --permission-mode acceptEdits --allowedTools "Bash Read Edit Write mcp__gitlab" --debug
variables:
AWS_REGION: "us-west-2"
CLAUDE_CODE_USE_BEDROCK: "1"sts assume-role-with-web-identityが返す一時的なアクセスキー・シークレットキー・セッショントークンを環境変数へ展開してから、claudeコマンドを呼ぶ流れです。例では3,600秒で切れます。ジョブが早く終わっても、発行した認証情報は期限まで有効なまま残るので、ジョブの長さに合わせて短めに設定しておくと安全です。
--duration-secondsに指定できるのは、900秒(15分)からロールに設定した最大セッション期間までです。ジョブがこれより長く走りそうなら、先にロール側の上限を確認します。この例では環境変数の値が固定されるため、期限が切れても自動では更新されません。
権限ポリシーとモデルID
Claude Codeのドキュメントにあるポリシー例は、bedrock:InvokeModelとbedrock:InvokeModelWithResponseStreamが中心です。推論プロファイルの解決にbedrock:ListInferenceProfilesとbedrock:GetInferenceProfileも与えます。Marketplaceの購読用にaws-marketplace:ViewSubscriptionsとaws-marketplace:Subscribeも加えます。この2つは、呼び出し元がBedrockのときだけ許可する条件付きです。リソースを推論プロファイルのARNに絞れば、さらに制限できます。
BedrockのモデルIDにはリージョン固有のプレフィックスが付きます(例: us.anthropic.claude-sonnet-4-6)。リージョンの決まり方は、AWS_REGION、AWS_DEFAULT_REGION、AWSプロファイルのregionの順で、どれも無ければus-east-1になります。地域名の形でない値は未設定として扱われます。CIではプロファイルを使わないので、例のようにAWS_REGIONを明示すると迷いません。使うモデルはジョブ設定かプロンプト経由で指定します。単価がClaude APIの直接契約とどう違うかはAWS BedrockのClaude料金にあります。
信頼ポリシーをどこまで絞るか
OIDC方式では、IAMロールの信頼ポリシーが唯一の門番です。トークンを持つジョブなら誰でもロールを引き受けられる設定にすると、キーレスにした意味が薄れます。
GitLabのIDトークンのsubクレームは、既定でproject_path:{group}/{project}:ref_type:{type}:ref:{branch_name}の形です。GitLab.comのOIDCプロバイダーでは、AWSの条件キーとして多くのクレームを使えます。namespace_id、project_id、ref_protected、pipeline_sourceなどです。一方、GitLab Self-ManagedとDedicatedではsubだけが条件キーになります。自社ホストのGitLabでは、subに含まれるプロジェクトパスとrefで絞る形になります。
GitLabのドキュメントは、パスだけに頼らずproject_idやnamespace_idのような不変のIDを併用するよう示しています。グループやプロジェクトの改名でパスが変わっても、IDは変わらないからです。user_loginやuser_emailは利用者が変更できるため、単独の条件には向きません。
次は、Self-Managedを想定した、audとsubで絞るAWSの標準的な信頼ポリシーの形です。この例はAWSの条件キーの書式に沿って組んだもので、Claude Codeのドキュメントには同じ記載がありません。アカウントID、ホスト名、プロジェクトパスは自分の環境の値に置き換えます。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::<ACCOUNT_ID>:oidc-provider/gitlab.example.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"gitlab.example.com:aud": "https://gitlab.example.com",
"gitlab.example.com:sub": "project_path:my-group/my-project:ref_type:branch:ref:main"
}
}
}
]
}ref:mainに固定した場合、フィーチャーブランチのパイプラインからはロールを引き受けられません。MRのレビューにClaudeを使う運用では、どのrefから動かすかを先に決めておく必要があります。
Google CloudのAgent PlatformでWorkload Identity Federationを組む手順
GCP側の下準備も4つです。
- IAM Credentials API・STS API・Google Cloud's Agent Platform APIの3つを有効化する
- GitLab OIDCを信頼するWorkload Identity Poolとプロバイダーを作成する
- Agent Platformの権限だけを持つ専用サービスアカウントを用意する
- そのサービスアカウントを借用(impersonate)できる権限をWIFプリンシパルへ与える
必要な変数は4つです。GCP_WORKLOAD_IDENTITY_PROVIDERは、プロバイダーのリソース名です。//iam.googleapis.com/プレフィックスは付けません。例はprojects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-providerです。残りはGCP_SERVICE_ACCOUNT(サービスアカウントのメールアドレス)、GCP_PROJECT_ID、CLOUD_ML_REGION(例: us-east5)です。
Bedrockのsts assume-roleと違い、GCP側はクレデンシャル設定ファイルをその場で組み立ててGOOGLE_APPLICATION_CREDENTIALSに渡します。GitLabのOIDCトークンをファイルへ書き出し、credential_sourceでそのファイルを指すJSONを生成する構成です。
claude-vertex:
stage: ai
image: gcr.io/google.com/cloudsdktool/google-cloud-cli:slim
id_tokens:
GITLAB_OIDC_TOKEN:
aud: https://gitlab.example.com
before_script:
- apt-get update && apt-get install -y git && apt-get clean
- curl -fsSL https://claude.ai/install.sh | bash
- export PATH="$HOME/.local/bin:$PATH"
- printf "%s" "$GITLAB_OIDC_TOKEN" > /tmp/oidc_token
- |
cat > /tmp/cred.json <<EOF
{
"type": "external_account",
"audience": "//iam.googleapis.com/${GCP_WORKLOAD_IDENTITY_PROVIDER}",
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
"token_url": "https://sts.googleapis.com/v1/token",
"credential_source": { "file": "/tmp/oidc_token" },
"service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${GCP_SERVICE_ACCOUNT}:generateAccessToken"
}
EOF
- export GOOGLE_APPLICATION_CREDENTIALS=/tmp/cred.json
- gcloud auth login --cred-file=/tmp/cred.json
- gcloud config set project "$GCP_PROJECT_ID"
script:
- /bin/gitlab-mcp-server || true
- claude -p "$AI_FLOW_INPUT" --permission-mode acceptEdits --allowedTools "Bash Read Edit Write mcp__gitlab" --debug
variables:
CLOUD_ML_REGION: "us-east5"
CLAUDE_CODE_USE_VERTEX: "1"
ANTHROPIC_VERTEX_PROJECT_ID: "$GCP_PROJECT_ID"Claude Codeは、Googleのアプリケーションデフォルト認証情報(ADC)の仕組みを通じてこのファイルを読みます。
サービスアカウントに付ける権限はroles/aiplatform.userで、モデル呼び出しとトークン数の計算に必要なaiplatform.endpoints.predictを含みます。もっと絞りたいときは、この権限だけのカスタムロールを作ります。
リージョンは、CLOUD_ML_REGIONにglobal、usやeuのようなマルチリージョン、us-east5のような個別リージョンを指定できます。値が地域名の形でなければ、us-east5にフォールバックします。globalを指定したとき、グローバルエンドポイントに対応していないモデルはVERTEX_REGION_CLAUDE_*変数で個別に上書きします。
プロジェクトIDは、ANTHROPIC_VERTEX_PROJECT_IDが優先されます。認証設定ファイルやGOOGLE_CLOUD_PROJECTが別のプロジェクトを指していても、リクエストはANTHROPIC_VERTEX_PROJECT_IDのプロジェクトに送られます。
AWSとGCPの認証設計の違い
信頼の置き場所と認証情報の持ち方
Amazon Bedrock
信頼はIAMロールのトラストポリシーに書きます。認証情報は一時的なアクセスキー・シークレットキー・セッショントークンの3点で、期限は--duration-secondsが決めます。CI/CD変数はAWS_ROLE_TO_ASSUMEとAWS_REGIONの2つで足ります。事前に有効化するのはモデルアクセスです。
Google CloudのAgent Platform
信頼はWorkload Identity Poolのプロバイダーに書きます。認証情報はADC経由のサービスアカウント借用トークンです。CI/CD変数は4つで、さらに認証設定ファイルをジョブ内で組み立てます。事前に有効化するAPIはIAM Credentials・STS・Agent Platformの3つです。
手順の数が増えるのはGCP側です。ただし、どちらも最終的にジョブが受け取るのは期限付きの一時的な認証情報で、サービスアカウントキーやアクセスキーを保管する必要がない点は共通です。
認証エラーの症状から原因を切り分ける
「AWS authentication failed」が出る
このエラーは、AWSが403を返したとき、またはBedrockが401を返したときに出ます。Bedrockは期限切れのトークンも、IAM権限の不足によるAccessDeniedExceptionも、同じ403で返します。Claude Code側ではこの2つを区別できません。
認証情報はジョブの開始時に発行したばかりなので、1時間を超える長いジョブでなければ、期限切れより権限の側から見ると切り分けが速くなります。ロールのポリシーにbedrock:InvokeModel系の許可があるか、対象モデルへのアクセスが有効になっているかを順に確かめます。モデルへのアクセスが無いことが原因の403なら、メッセージのヒントがAmazon Bedrockコンソールでモデルを有効化するよう案内します。
assume-role-with-web-identityが拒否される
GitLabのドキュメントは、OIDC認証で401が出る典型原因を4つ挙げています。非推奨のCI_JOB_JWT_V2を使っている、.gitlab-ci.ymlと外部サービスでprovider_nameが食い違っている。audが一致していない。id_tokens:ブロックを設定していない。
ジョブの中でトークンを復号して確認する方法も示されています。echo $GITLAB_OIDC_TOKEN | cut -d '.' -f2 | base64 -d | jq .でaudとsubを見て、AWS側の信頼ポリシーの値と並べます。表示するのはペイロード部分だけですが、ジョブのログに残るので、デバッグが終わったらこの行を外します。
GitLabが「ID token issuance is disabled」と返す
プロジェクトパスを含むsubが、以前に別のプロジェクトが使っていたパスと重なると、GitLabはトークンの発行自体を止めます。プロジェクトパスを再利用した場合などが該当します。対処は、プロジェクトIDをsubの先頭に据えるci_id_token_sub_claim_componentsの設定で、AWS側の信頼ポリシーもその新しい形式に合わせて更新します。
on-demand throughput isn't supportedが出る
モデルIDを推論プロファイルのIDで指定していないときに出ます。us.anthropic.…のようなプレフィックス付きのIDを使います。リージョンごとの利用可能なモデルは、aws bedrock list-inference-profiles --region <リージョン>で調べられます。プロファイルの一覧を取れない状態では、Claude Codeはプレフィックスだけを機械的に付けます。そのプレフィックスの推論プロファイルが有効でないアカウントでは、400になります。
Agent Platformで429やモデル未提供のエラーになる
リージョナルエンドポイントでは、メインモデルと軽量モデルの両方が選んだリージョンで使える必要があります。可用性が不安定ならCLOUD_ML_REGION=globalを試す選択肢があります。モデルによってはグローバルエンドポイントに未対応なので、Model Gardenの「Supported features」で確認します。
静的キーよりOIDC/WIFを選ぶ理由
CI/CDに長期有効なクラウド認証情報を変数として置くと、変数を読める人は全員そのキーに到達できます。OIDC/WIFなら、ジョブごとに発行された認証情報に置き換わるので、ローテーションの運用が要りません。漏れたときに奪われるのは、期限が切れるまでの間だけ有効な認証情報です。
トークンのクレームも手がかりになります。GitLabのIDトークンには、project_path、ref、ref_protected、pipeline_id、job_id、user_loginなどが入ります。どのクレームがクラウド側の監査ログに残るかは、CloudTrailやGoogle Cloud Audit Logsの仕様次第です。導入前に、実際のログで追えるかを確認します。
ジョブに与える権限と、ネットワークの送信先を絞る考え方は、Claude Codeの権限モードの原則(Claude Codeセキュリティ・権限ガイド)と同じ方向です。CIでCloudFormationやCDKを扱うなら、aws-iac-mcp-serverの使い方がテンプレート検証とデプロイ失敗の診断をMCP経由で行う実装例になります。GitLab以外のCIでの失敗診断はCircleCI・Buildkite・JenkinsのMCP比較にあります。
手元のclaudeで確認できること
YAMLで使う-p、--permission-mode、--allowedToolsの3つのフラグは、v2.1.287のclaude --helpに載っています。--permission-modeの選択肢はacceptEdits、auto、bypassPermissions、manual、dontAsk、planの6つです。--allowedToolsは、カンマまたは空白区切りでツール名を並べる指定です。
CIの暴走を抑える手段として、--max-budget-usdもあります。ヘルプには「API呼び出しの上限額(--printのときだけ有効)」とあり、-pで動かすこのジョブにそのまま付けられます。ジョブ側のtimeoutと併用すれば、時間と金額の両方に上限をかけられます。エージェントのターン数なら、CLI referenceにある--max-turns(printモード専用)で上限を決められます。こちらはclaude --helpには出ません。
ローカルで対話的にBedrockを試す場合は、Claude Code Bedrockセットアップの/setup-bedrockウィザードが入り口です。CI環境のOIDCとは別の経路になります。
まとめ
AWSではまず信頼ポリシーのaudとsubを決め、GCPでは認証設定ファイルの組み立てを確認する、という順に進めると、認証エラーの原因をトークンの中身に絞って追えます。自社ホストのGitLabなら、AWS側の条件がsubだけになる点を最初に押さえておくと、設計のやり直しを避けられます。