Workload Identity Federation
GitHub Actions・Google Cloud Run・AWS Lambda などの外部ワークロードが発行する短命トークンを使って、 静的なアクセストークンなしで CRISIS API にアクセスする仕組みです。 シークレットをリポジトリや環境変数に保存する必要がなくなります。
概要
従来の認証では、静的なアクセストークンを Secret Manager などのシークレット管理サービスに保存し、CI/CD から取得して利用していました。 静的トークンは一度漏洩すると無期限に悪用される可能性があります。
従来の認証フロー
CI/CD ワークロード
─── 静的トークン(無期限)を Secret Manager 等から取得
──────────────────────────▶ CRISIS API
WIF による認証フロー
CI/CD ワークロード
─── IdP から短命トークンを自動取得
─── Token Exchange で CRISIS トークンを取得
──────────────────────────▶ CRISIS API
- シークレット不要 — 静的トークンをリポジトリや環境変数に保存する必要がありません
- 短命トークン — 発行されるアクセストークンの有効期限は最大 1 時間
- 漏洩リスクの低減 — トークンが漏洩しても、被害範囲が時間的に限定されます
- マルチプロバイダー対応 — OIDC 対応プロバイダー(GitHub Actions, Google Cloud, Azure AD, AWS 等)をサポート
仕組み
CRISIS は受け取った外部トークンの内容を Federated Credential の設定値と照合し、 すべての条件を満たした場合に対応するサービスアカウントのアクセストークンを発行します。
検証フロー
CRISIS は受け取った OIDC JWT に対して、以下の条件をすべて検証します。
issuer_url — JWT の iss クレームと一致すること
audience — JWT の aud クレームと一致すること
subject_filter — JWT の sub クレームと一致すること
attribute_condition(設定した場合) — 指定した式が true を返すこと
条件を満たさない場合に返るエラーの詳細はエラーレスポンスを参照してください。
セットアップ
1. サービスアカウントの作成
CRISIS の管理画面または API でサービスアカウントを作成します。 WIF を使うサービスアカウントは、従来の静的アクセストークンとの共存も可能です。 すでに適切なサービスアカウントがある場合は、この手順をスキップできます。
2. Federated Credential の作成
サービスアカウントに Federated Credential を登録します。 これにより、指定した外部 IdP が認証するワークロードが、そのサービスアカウントとして認証できるようになります。
GitHub Actions の場合
POST /organizations/{organizationId}/service_accounts/{serviceAccountId}/federated_credentials
{
"display_name": "GitHub Actions (my-org/my-repo)",
"issuer_url": "https://token.actions.githubusercontent.com",
"subject_filter": "repo:my-org/my-repo:ref:refs/heads/main",
"audience": "https://crisis.jp",
"status": "ACTIVE"
}
AWS の場合
AWS IAM Outbound Identity Federation(GetWebIdentityToken API)を使用します。
AWS STS がワークロードの IAM ID をアサートする OIDC JWT を発行します。
POST /organizations/{organizationId}/service_accounts/{serviceAccountId}/federated_credentials
{
"display_name": "AWS Lambda (crisis-integration)",
"issuer_url": "https://<uuid>.tokens.sts.global.api.aws",
"subject_filter": "arn:aws:iam::123456789012:role/crisis-integration-role",
"audience": "https://crisis.jp",
"status": "ACTIVE"
}
issuer_url には AWS アカウント固有の STS 発行者 URL を指定します。
IAM コンソールの「アカウント設定」または CLI で確認できます(詳細は使用例: AWS Lambdaを参照)。
subject_filter にはワークロードの IAM ロール ARN(GetWebIdentityToken が発行する JWT の sub クレーム)を指定します。
フィールド
display_name (必須) —
管理用のラベル
issuer_url (必須) —
IdP の発行者 URL(https:// 必須、作成時に OIDC Discovery で検証)
subject_filter (必須) —
信頼する sub クレームの値(完全一致またはワイルドカード * 接尾)
audience —
期待する aud クレーム値。省略時は https://crisis.jp
attribute_condition —
追加の属性条件式(後述)
status —
ACTIVE または INACTIVE。省略時は ACTIVE
注意:
issuer_url・subject_filter・audience
は作成後は変更できません。
変更が必要な場合は削除して再作成してください。
attribute_condition について
sub 以外のクレームで追加の制約をかけたい場合に使用します。
構文は expr-lang に準拠した式言語をサポートしています。
利用可能な演算子
==, != — 等価・非等価startsWith, endsWith, contains — 文字列マッチin — 配列に含まれるか: account in ["123", "456"]&&, || — 論理 AND / OR!() — 否定例
// 単一条件: issuer が一致すること
"attribute_condition": "iss == \"https://token.actions.githubusercontent.com\""
// 複合条件: issuer と ref ブランチの両方を検証
"attribute_condition": "iss == \"https://token.actions.githubusercontent.com\" && ref == \"refs/heads/main\""
// 部分一致: email がドメインで終わること
"attribute_condition": "email endsWith \"@example.com\""
例(AWS STS トークン)
AWS STS GetWebIdentityToken が発行する JWT には、標準クレーム(sub = IAM ロール ARN)に加えて、
https://sts.amazonaws.com/ ネームスペースのクレームが含まれます。
これらを attribute_condition で参照できます:
sub — IAM ロール ARN(例: "arn:aws:iam::123456789012:role/my-role")["https://sts.amazonaws.com/"]["aws_account"] — AWS アカウント ID(例: "123456789012")["https://sts.amazonaws.com/"]["principal_id"] — プリンシパル ID(例: "AROAEXAMPLE")// 特定の AWS アカウントのみ許可
"attribute_condition": "[\"https://sts.amazonaws.com/\"][\"aws_account\"] == \"123456789012\""
// 複数アカウントを許可
"attribute_condition": "[\"https://sts.amazonaws.com/\"][\"aws_account\"] in [\"123456789012\", \"987654321098\"]"
// IAM ロール ARN のプレフィックスで制約
"attribute_condition": "sub startsWith \"arn:aws:iam::123456789012:role/\""
利用できるクレーム名は IdP ごとに異なります。GitHub Actions の場合は OIDC トークンの仕様 を参照してください。
作成レスポンスに含まれる id が credentialId です。
トークン交換時のエンドポイント URL に使用します。
トークン交換
外部 IdP から取得した OIDC JWT を提示し、CRISIS アクセストークンと交換します。 エンドポイントは RFC 8693(OAuth 2.0 Token Exchange)に準拠しています。
エンドポイント
POST https://auth.crisis.jp/workload-identity/{credentialId}/token
Content-Type: application/x-www-form-urlencoded
リクエストパラメータ(form-urlencoded)
grant_type — urn:ietf:params:oauth:grant-type:token-exchange(固定値)
subject_token — 外部 IdP が発行した署名済み OIDC JWT
subject_token_type — urn:ietf:params:oauth:token-type:jwt(固定値)
レスポンス
{
"access_token": "<CRISIS アクセストークン>",
"issued_token_type": "urn:ietf:params:oauth:token-type:access_token",
"token_type": "Bearer",
"expires_in": 3600
}
取得した access_token を Bearer トークンとして通常どおり CRISIS API に渡します。
有効期限は 1 時間(3600 秒) で、トークン交換エンドポイントは認証不要・レート制限の対象です。
使用例: GitHub Actions
GitHub Actions の OIDC トークンを使って CRISIS API を呼び出す例です。 リポジトリにシークレットを一切設定する必要がありません。
Federated Credential の設定
{
"display_name": "GitHub Actions (my-org/my-repo main branch)",
"issuer_url": "https://token.actions.githubusercontent.com",
"subject_filter": "repo:my-org/my-repo:ref:refs/heads/main",
"audience": "https://crisis.jp"
}
subject_filter の値は
GitHub
の OIDC トークン仕様
に基づきます。
repo:my-org/my-repo:ref:refs/heads/main — main ブランチのプッシュ
repo:my-org/my-repo:ref:refs/tags/v* — タグプッシュrepo:my-org/my-repo:environment:production — Environment
production のジョブ
repo:my-org/my-repo:pull_request — プルリクエストattribute_condition の推奨設定
subject_filter に加えて、repository_owner クレームで組織を制約することを推奨します。
これにより、万が一 subject_filter のワイルドカードが広すぎる場合でも、
信頼する組織外のリポジトリからのトークン交換を防止できます。
// リポジトリオーナー(組織)を制約
"attribute_condition": "repository_owner == \"my-org\""
// 組織 + 特定の環境に制約
"attribute_condition": "repository_owner == \"my-org\" && environment == \"production\""
ワークフロー例
name: Deploy
on:
push:
branches: [main]
permissions:
id-token: write # OIDC トークンの取得に必要
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Get OIDC token and exchange for CRISIS token
run: |
# GitHub Actions から OIDC JWT を取得
OIDC_JWT=$(curl -sSfL \
-H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
-H "Accept: application/json; api-version=2.0" \
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=https://crisis.jp" \
| jq -r '.value')
# CRISIS Token Exchange
CRISIS_TOKEN=$(curl -sSf \
-X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
--data-urlencode "subject_token=${OIDC_JWT}" \
--data-urlencode "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \
"https://auth.crisis.jp/workload-identity/${{ vars.CRISIS_CREDENTIAL_ID }}/token" \
| jq -r '.access_token')
echo "::add-mask::${CRISIS_TOKEN}"
# 同一ステップ内で API を呼び出し、トークンを外部に渡さない
curl -sSf \
-H "Authorization: Bearer ${CRISIS_TOKEN}" \
https://api.crisis.jp/v1/organizations/${{ vars.CRISIS_ORG_ID }}/incidents
セキュリティ上のヒント: 取得したアクセストークンは可能な限り同一ステップ内で使い切るのが安全です。
他ステップへ渡す必要がある場合は $GITHUB_OUTPUT ではなく $GITHUB_ENV(同一 Job 内に限定)を使用し、artifact
として保存しないでください。
設定すべき Variables
CRISIS_CREDENTIAL_ID — 作成した FederatedCredential の idCRISIS_ORG_ID — 組織 IDシークレットは一切不要です。Variables(非機密設定値)のみで動作します。
使用例: Google Cloud
Google Cloud のサービスアカウントが発行する ID Token を使う例です。 Cloud Run などのマネージド環境で、Metadata Server からトークンを取得できます。
Federated Credential の設定
{
"display_name": "Google Cloud Run (my-service)",
"issuer_url": "https://accounts.google.com",
"subject_filter": "123456789012345678901",
"audience": "https://crisis.jp"
}
subject_filter には Google Cloud サービスアカウントの Unique ID(21 桁前後の数字)を指定します。
Google Cloud Console の「IAM と管理 → サービスアカウント」で対象サービスアカウントの詳細画面を開き、
「一意の ID」欄に表示される値です。
attribute_condition の推奨設定
サービスアカウントが削除・再作成された場合、sub(Unique ID)は変わりますが、
同じメールアドレスが再利用される可能性があります。
逆に sub のみでの制約では、意図したサービスアカウントであることの可読性が低くなります。
email クレームを attribute_condition で併用することで、
より明確かつ安全な制約が可能です。
// email クレームでサービスアカウントを明示的に検証
"attribute_condition": "email == \"my-service@my-project.iam.gserviceaccount.com\""
// email ドメインでプロジェクト内の全 SA を許可
"attribute_condition": "email endsWith \"@my-project.iam.gserviceaccount.com\""
注意: email クレームを取得するには、Metadata Server へのリクエストに
format=full パラメータを付与する必要があります(下記コード例参照)。
format=full を付けない場合、トークンに email クレームは含まれません。
アクセストークンの取得
# Cloud Run 上のサービスから実行する場合
# CRISIS_CREDENTIAL_ID は環境変数として事前に設定しておく
# format=full を付けると email クレームがトークンに含まれる
GOOGLE_TOKEN=$(curl -sSf \
-H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://crisis.jp&format=full")
CRISIS_TOKEN=$(curl -sSf \
-X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
--data-urlencode "subject_token=${GOOGLE_TOKEN}" \
--data-urlencode "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \
"https://auth.crisis.jp/workload-identity/${CRISIS_CREDENTIAL_ID}/token" \
| jq -r '.access_token')
使用例: Azure
Azure のマネージド ID(Managed Identity)が発行する OIDC トークンを使って CRISIS API を呼び出す例です。 CRISIS のシークレットを Azure に保存する必要はありません。
Federated Credential の設定
{
"display_name": "Azure Functions (crisis-integration)",
"issuer_url": "https://login.microsoftonline.com/{tenant-id}/v2.0",
"subject_filter": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"audience": "api://crisis-wif"
}
issuer_url には Azure AD テナントの OIDC 発行者 URL を指定します。
subject_filter には Managed Identity の Object ID(Principal ID) を指定します。
Azure Portal の「マネージド ID」画面で確認できます。
issuer_url の形式について:
https://login.microsoftonline.com/{tenant-id}/v2.0— v2.0 エンドポイント(推奨)https://sts.windows.net/{tenant-id}/— v1.0 エンドポイント(レガシー)
発行されるトークンの iss クレームと一致する方を指定してください。
Azure Functions / App Service / AKS の新規構成では通常 v2.0 です。
attribute_condition の例
// Object ID(sub)で制約する場合は subject_filter で十分だが、
// テナント ID でも二重に検証したい場合:
"attribute_condition": "tid == \"your-tenant-id\""
// 複数のマネージド ID を許可する場合:
"attribute_condition": "oid in [\"object-id-1\", \"object-id-2\"]"
Azure Functions の例(Python)
import json
import os
import urllib.request
import urllib.parse
import azure.functions as func
from azure.identity import ManagedIdentityCredential
CREDENTIAL_ID = os.environ["CRISIS_CREDENTIAL_ID"]
AUDIENCE = os.environ.get("CRISIS_AUDIENCE", "api://crisis-wif")
def main(req: func.HttpRequest) -> func.HttpResponse:
# Step 1: Managed Identity から OIDC トークンを取得
credential = ManagedIdentityCredential()
azure_token = credential.get_token(AUDIENCE).token
# Step 2: CRISIS Token Exchange
crisis_token = exchange_token(CREDENTIAL_ID, azure_token)
# Step 3: CRISIS API を呼び出す
api_req = urllib.request.Request(
"https://api.crisis.jp/v1/organizations/YOUR_ORG_ID/incidents",
headers={"Authorization": f"Bearer {crisis_token}"},
)
with urllib.request.urlopen(api_req) as resp:
data = json.loads(resp.read())
return func.HttpResponse(json.dumps(data), mimetype="application/json")
def exchange_token(credential_id: str, subject_token: str) -> str:
"""CRISIS Token Exchange エンドポイントを呼び出す"""
data = urllib.parse.urlencode({
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token": subject_token,
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
}).encode()
req = urllib.request.Request(
f"https://auth.crisis.jp/workload-identity/{credential_id}/token",
data=data,
headers={"Content-Type": "application/x-www-form-urlencoded"},
method="POST",
)
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read())["access_token"]
Azure Functions の例(Node.js)
import { app } from "@azure/functions";
import { ManagedIdentityCredential } from "@azure/identity";
const CREDENTIAL_ID = process.env.CRISIS_CREDENTIAL_ID;
const AUDIENCE = process.env.CRISIS_AUDIENCE || "api://crisis-wif";
app.http("crisisProxy", {
methods: ["GET"],
handler: async (request, context) => {
// Step 1: Managed Identity から OIDC トークンを取得
const credential = new ManagedIdentityCredential();
const { token: azureToken } = await credential.getToken(AUDIENCE);
// Step 2: CRISIS Token Exchange
const body = new URLSearchParams({
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
subject_token: azureToken,
subject_token_type: "urn:ietf:params:oauth:token-type:jwt",
});
const tokenRes = await fetch(
`https://auth.crisis.jp/workload-identity/${CREDENTIAL_ID}/token`,
{ method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body }
);
const { access_token } = await tokenRes.json();
// Step 3: CRISIS API を呼び出す
const apiRes = await fetch(
"https://api.crisis.jp/v1/organizations/YOUR_ORG_ID/incidents",
{ headers: { Authorization: `Bearer ${access_token}` } }
);
return { jsonBody: await apiRes.json() };
},
});
AKS (Azure Kubernetes Service) の例
AKS Workload Identity を使う場合、Pod に自動マウントされる Projected Service Account Token を利用します。 Azure Workload Identity webhook がトークンファイルのパスを環境変数で注入します。
# AKS Pod 内のシェルスクリプト例
# Azure Workload Identity が注入する環境変数:
# AZURE_FEDERATED_TOKEN_FILE — トークンファイルのパス
# AZURE_AUTHORITY_HOST — https://login.microsoftonline.com/
# AZURE_TENANT_ID — テナント ID
# AZURE_CLIENT_ID — マネージド ID のクライアント ID
# Projected SA Token を読み取り(audience は Azure AD 向け)、
# Azure AD から ID Token を取得し、CRISIS に交換する
# azure-identity SDK を使う場合は自動的にファイルから読み取る
# 手動で行う場合:
AZURE_TOKEN=$(cat "$AZURE_FEDERATED_TOKEN_FILE")
# Azure AD にトークン交換して ID Token を取得
AZURE_ID_TOKEN=$(curl -sSf -X POST \
"${AZURE_AUTHORITY_HOST}${AZURE_TENANT_ID}/oauth2/v2.0/token" \
-d "client_id=${AZURE_CLIENT_ID}" \
-d "scope=api://crisis-wif/.default" \
-d "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer" \
-d "client_assertion=${AZURE_TOKEN}" \
-d "grant_type=client_credentials" \
| jq -r '.access_token')
# CRISIS Token Exchange
CRISIS_TOKEN=$(curl -sSf -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:token-exchange" \
--data-urlencode "subject_token=${AZURE_ID_TOKEN}" \
--data-urlencode "subject_token_type=urn:ietf:params:oauth:token-type:jwt" \
"https://auth.crisis.jp/workload-identity/${CRISIS_CREDENTIAL_ID}/token" \
| jq -r '.access_token')
curl -H "Authorization: Bearer ${CRISIS_TOKEN}" \
https://api.crisis.jp/v1/organizations/${CRISIS_ORG_ID}/incidents
必要なアプリケーション設定
CRISIS_CREDENTIAL_ID — 作成した FederatedCredential の idCRISIS_AUDIENCE — Federated Credential に設定した
audience(デフォルト:
api://crisis-wif)
Azure 側の前提条件
api://crisis-wif を Audience として受け付ける App Registration が Azure AD に登録されていること
シークレット不要: CRISIS のアクセストークンや API キーを Azure Key Vault やアプリケーション設定に保存する必要は一切ありません。 Azure のマネージド ID 自体が CRISIS への認証手段となります。
使用例: AWS Lambda
AWS Lambda 関数から CRISIS API を呼び出す例です。
AWS IAM Outbound Identity Federation(GetWebIdentityToken API)で OIDC JWT を取得し、
他のプロバイダーと同じトークン交換フローで CRISIS アクセストークンを取得します。
CRISIS のシークレットを AWS に保存する必要はありません。
AWS 側の前提条件
aws iam enable-outbound-web-identity-federation
sts:GetWebIdentityToken の呼び出し権限を付与します:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["sts:GetWebIdentityToken"],
"Resource": "*"
}
]
}
https://<uuid>.tokens.sts.global.api.aws)が割り当てられます。
IAM コンソールの「アカウント設定」の「Get Token Issuer URL」欄、または CLI で確認できます:
aws iam get-outbound-web-identity-federation-info \
--query 'IssuerUrl' --output text
Federated Credential の設定
{
"display_name": "AWS Lambda (crisis-integration)",
"issuer_url": "https://<uuid>.tokens.sts.global.api.aws",
"subject_filter": "arn:aws:iam::123456789012:role/crisis-integration-role",
"audience": "https://crisis.jp"
}
issuer_url には上記で確認した AWS アカウント固有の STS 発行者 URL を指定します。
subject_filter には Lambda 実行ロールの IAM ロール ARN を指定します
(GetWebIdentityToken が発行する JWT の sub クレーム)。
arn:aws:iam::123456789012:role/my-role — 特定ロールに制限(推奨)arn:aws:iam::123456789012:role/* — アカウント内の全ロールを許可(非推奨)Lambda 関数の例(Python)
import json
import os
import urllib.request
import urllib.parse
import boto3
CREDENTIAL_ID = os.environ["CRISIS_CREDENTIAL_ID"]
REGION = os.environ.get("AWS_REGION", "us-east-1")
def lambda_handler(event, context):
# Step 1: STS から OIDC JWT を取得
sts = boto3.client("sts", region_name=REGION)
resp = sts.get_web_identity_token(
Audience=["https://crisis.jp"],
SigningAlgorithm="RS256",
DurationSeconds=900,
)
subject_token = resp["WebIdentityToken"]
# Step 2: CRISIS Token Exchange
crisis_token = exchange_token(CREDENTIAL_ID, subject_token)
# Step 3: CRISIS API を呼び出す
req = urllib.request.Request(
"https://api.crisis.jp/v1/organizations/YOUR_ORG_ID/incidents",
headers={"Authorization": f"Bearer {crisis_token}"},
)
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read())
def exchange_token(credential_id: str, subject_token: str) -> str:
"""CRISIS Token Exchange エンドポイントを呼び出す"""
data = urllib.parse.urlencode({
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token": subject_token,
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
}).encode()
req = urllib.request.Request(
f"https://auth.crisis.jp/workload-identity/{credential_id}/token",
data=data,
headers={"Content-Type": "application/x-www-form-urlencoded"},
method="POST",
)
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read())["access_token"]
Lambda 関数の例(Node.js)
import { STSClient, GetWebIdentityTokenCommand } from "@aws-sdk/client-sts";
const CREDENTIAL_ID = process.env.CRISIS_CREDENTIAL_ID;
const REGION = process.env.AWS_REGION || "us-east-1";
export async function handler(event) {
// Step 1: STS から OIDC JWT を取得
const sts = new STSClient({ region: REGION });
const { WebIdentityToken } = await sts.send(
new GetWebIdentityTokenCommand({
Audience: ["https://crisis.jp"],
SigningAlgorithm: "RS256",
DurationSeconds: 900,
})
);
// Step 2: CRISIS Token Exchange
const body = new URLSearchParams({
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
subject_token: WebIdentityToken,
subject_token_type: "urn:ietf:params:oauth:token-type:jwt",
});
const tokenRes = await fetch(
`https://auth.crisis.jp/workload-identity/${CREDENTIAL_ID}/token`,
{ method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body }
);
const { access_token } = await tokenRes.json();
// Step 3: CRISIS API を呼び出す
const apiRes = await fetch(
"https://api.crisis.jp/v1/organizations/YOUR_ORG_ID/incidents",
{ headers: { Authorization: `Bearer ${access_token}` } }
);
return await apiRes.json();
}
必要な Lambda 環境変数
CRISIS_CREDENTIAL_ID — 作成した FederatedCredential の idIAM ロールのポリシー
Lambda の実行ロールには sts:GetWebIdentityToken の呼び出し権限が必要です。
Audience 条件キーで許可する audience を制限することを推奨します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["sts:GetWebIdentityToken"],
"Resource": "*",
"Condition": {
"StringEquals": {
"sts:Audience": "https://crisis.jp"
}
}
}
]
}
注意: GetWebIdentityToken はリージョナル STS エンドポイントでのみ利用可能です。
STS クライアントのリージョンを明示的に指定してください(例: us-east-1)。
シークレット不要: CRISIS のアクセストークンや API キーを AWS Secrets Manager や Lambda の環境変数に保存する必要は一切ありません。 Lambda の実行ロール自体が CRISIS への認証手段となります。
エラーレスポンス
トークン交換が失敗した場合、RFC 6749 §5.2 に準拠した JSON 形式でエラーが返ります。
error フィールドの値で失敗理由を判別できます。
{
"error": "invalid_token",
"error_description": "Token verification failed"
}
invalid_request —
リクエストパラメータ不正(grant_type 間違い、subject_token_type 未対応など)
invalid_token —
JWT の署名検証失敗・有効期限切れ・iss/aud 不一致
access_denied —
sub が subject_filter に不一致、
attribute_condition 不満足
セキュリティのベストプラクティス
- 最小権限のサービスアカウントを使う — WIF 用のサービスアカウントには、ワークロードが必要とする最小限のロールのみを付与してください。 発行されるアクセストークンはそのサービスアカウントの権限範囲に限定されます
- 短命トークンを活かす — 発行されるアクセストークンの有効期限は最大 1 時間です。 トークンをファイルやデータベースに永続化せず、必要なタイミングで都度交換してください
-
subject_filterを可能な限り限定する — ワイルドカード(*)の使用範囲を最小にし、特定のワークロードのみがトークン交換を行えるように制約してください -
attribute_conditionで追加の制約をかける —subject_filterだけでは不十分な場合、環境名・ブランチ・アカウント ID などの追加条件を設定できます - AWS: Outbound Identity Federation は必要なアカウントのみで有効化する — Outbound WIF はアカウント単位の設定です。 必要なアカウントでのみ有効化し、不要なアカウントでは無効のままにしてください
-
AWS: IAM ポリシーで
Audienceを制限する —sts:GetWebIdentityTokenの IAM ポリシーでsts:Audience条件キーを使用し、 許可する audience を制限することで、意図しないトークン発行を防止できます - 不要になった Credential は削除する — 使われなくなった Federated Credential は速やかに削除し、認証経路を最小限に保ってください