はじめに
Amazon Bedrock AgentCore でマルチテナント設計をする際に属性ベースのアクセス制御(以下、ABAC)を検証した内容を、実際の手順とコードを踏まえつつまとめます。
使用するリソース
- Amazon Cognito User Pool: ユーザー認証基盤
- Amazon S3 Bucket: マルチテナントを想定したストレージ
- プレフィックス構造:
tenant-a/,tenant-b/
- プレフィックス構造:
- Amazon Bedrock AgentCore Runtime: エージェント実行環境
アーキテクチャ(ざっくり)
本実装では、以下のようなアーキテクチャで ABAC を実現しています。
AgentCore Runtime は Strands Agents で実装し、S3 にアクセスするための簡易的なツールを持ちます。
リポジトリ
以下に Amazon AgentCore Runtime のコード、デプロイ用の CDK を含む、サンプルコードを配置しています。
Amazon AgentCore Runtime は Python、CDK は TypeScript で記述します。
CDK による Amazon AgentCore のデプロイには 2026/03 現在 alpha 版となっている L2 Construct を使用しています。
今後の変更により、記事に記載しているコードは動作しない可能性がありますのでご注意ください。
なお、 L2 Construct については以下の記事で紹介しています。
ABAC (属性ベースのアクセス制御) の実装
属性ベースのアクセス制御を実現するためには、大きく分けて以下の 5つのステップを踏む必要があります。
- ABAC 用の IAM(ロール、ポリシー)を作成する
- Cognito のユーザーにテナントを識別するための任意のユーザー属性(今回は
custom:tenant_id)を付与する - Cognito の Pre Token Generation Lambda を実装し、認証時にユーザー属性を ID トークンとアクセストークンのクレームに付与する
- Bedrock AgentCore Runtime でカスタムヘッダーの受け取りを許可する
- Bedrock AgentCore Runtime でカスタムヘッダーから受け取った ID トークンの情報を用いて、作成した IAM ロールへとセッションタグを用いて AssumeRole し処理を実行する
この 5つのステップを順を追ってリポジトリのコードを抜粋しつつ、解説したいと思います。
ABAC 用の IAM の作成
まずは、ABAC のコアとなる IAM の作成です。今回は CDK で作成します。
ポイントは、IAM ポリシーの Condition に ${aws:PrincipalTag/custom:tenant_id} を使用することで、ユーザーのセッションタグに含まれる custom:tenant_id 属性と S3 オブジェクトのタグを照合し、アクセス制御を行います。
// ABAC用のIAMロールを作成
// AgentCore Runtime の実行ロールから AssumeRole でセッションタグを付与する方式を採用
this.abacRole = new iam.Role(this, "ABACTenantRole", {
roleName: "ABACTenantRole",
assumedBy: new iam.AccountRootPrincipal(),
description: "Role for ABAC with tag-based access control",
});
// S3オブジェクトのタグとPrincipalTagを照合するポリシー
// リソースパスにもPrincipalTagを使用してテナントフォルダへのアクセスを制限
this.abacRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
"s3:GetObject",
],
resources: [`${this.multiTenantBucket.bucketArn}/\${aws:PrincipalTag/custom:tenant_id}/*`],
conditions: {
StringEquals: {
// S3オブジェクトのtenant_idタグとPrincipalTagのcustom:tenant_idを照合
"s3:ExistingObjectTag/tenant_id": "${aws:PrincipalTag/custom:tenant_id}",
},
},
}),
);
// ListBucket権限(プレフィックスでテナントフォルダに制限)
this.abacRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["s3:ListBucket"],
resources: [this.multiTenantBucket.bucketArn],
conditions: {
StringLike: {
"s3:prefix": [
"",
"${aws:PrincipalTag/custom:tenant_id}",
"${aws:PrincipalTag/custom:tenant_id}/*",
]
}
},
}),
);
Pre Token Generation Lambda の実装
Cognito の Pre Token Generation Lambda トリガー(V3_0)を使用して、ユーザー属性の custom:tenant_id を ID トークンとアクセストークンの両方にクレームとして追加します。
def lambda_handler(event, context):
"""
Cognito Pre Token Generation Lambda Trigger (V3_0)
ユーザー属性からテナントIDを取得し、アクセストークンとIDトークンに追加
"""
# ユーザー属性からテナントIDを取得
tenant_id = event.get('request', {}).get('userAttributes', {}).get('custom:tenant_id')
# V3_0 形式でアクセストークンとIDトークンにテナントIDを追加
event['response'] = {
'claimsAndScopeOverrideDetails': {
'idTokenGeneration': {
'claimsToAddOrOverride': {
'custom:tenant_id': tenant_id
}
},
'accessTokenGeneration': {
'claimsToAddOrOverride': {
'custom:tenant_id': tenant_id
},
'scopesToAdd': []
}
}
}
return event
カスタムヘッダーの伝播設定
AgentCore Runtime では、デフォルトではリクエストヘッダーの情報を取得できません。
ID トークンをカスタムヘッダーで受け取るため、AgentCore Runtime の requestHeaderConfiguration に X-Amzn-Bedrock-AgentCore-Runtime-Custom-IdToken を含める必要があります。
また、AgentCore Runtime の実行ロールに sts:TagSession 権限を付与し、ABAC ロールへの AssumeRole を許可します。
const abacAgentRuntime = new agentcore.Runtime(this, "AbacAgentRuntime", {
runtimeName: "abac_agent",
agentRuntimeArtifact: abacAgentRuntimeArtifact,
description: "ABAC access-controlled agents",
authorizerConfiguration: agentcore.RuntimeAuthorizerConfiguration.usingCognito(
this.userPool,
[this.userPoolClient]
),
requestHeaderConfiguration: {
// カスタムヘッダーでIDトークンを受け取る
allowlistedHeaders: ["Authorization", "X-Amzn-Bedrock-AgentCore-Runtime-Custom-IdToken"]
},
environmentVariables: {
USER_POOL_ID: this.userPool.userPoolId,
USER_CLIENT_ID: this.userPoolClient.userPoolClientId,
BUCKET_NAME: this.multiTenantBucket.bucketName,
ABAC_ROLE_ARN: this.abacRole.roleArn, // ABACロールのARNを環境変数として渡す
},
});
// AgentCore Runtime の実行ロールに必要な権限を追加
abacAgentRuntime.addToRolePolicy(bedrockInvokePolicy)
abacAgentRuntime.addToRolePolicy(new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["sts:AssumeRole", "sts:TagSession"],
resources: ["*"],
}))
// ABAC ロールの信頼ポリシーを更新
this.abacRole.assumeRolePolicy?.addStatements(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["sts:AssumeRole", "sts:TagSession"],
principals: [new iam.AccountRootPrincipal()],
})
);
AgentCore Runtime での AssumeRole とセッションタグの実装
最後のステップとして、AgentCore Runtime 側で受け取った ID トークンから custom:tenant_id を抽出し、セッションタグとして付与しながら ABAC ロールへ AssumeRole を実行します。
このとき、Tags パラメータで明示的にセッションタグを設定することで、PrincipalTag/custom:tenant_id として IAM ポリシーの条件で使用できるようになります。
def get_s3_client_with_assumed_role(id_token: str) -> boto3.client:
sts_client = boto3.client("sts")
try:
tenant_id = get_tenant_id_from_token(id_token)
# 一段階のAssumeRoleでセッションタグを設定
response = sts_client.assume_role(
RoleArn=ABAC_ROLE_ARN,
RoleSessionName="abac-agent-session",
Tags=[{"Key": "custom:tenant_id", "Value": tenant_id}],
)
# 一時的な認証情報でS3クライアントを作成
credentials = response["Credentials"]
s3_client = boto3.client(
"s3",
aws_access_key_id=credentials["AccessKeyId"],
aws_secret_access_key=credentials["SecretAccessKey"],
aws_session_token=credentials["SessionToken"],
)
return s3_client
except Exception as e:
raise e
エージェントのエントリーポイントでは、リクエストコンテキストからカスタムヘッダー X-Amzn-Bedrock-AgentCore-Runtime-Custom-IdToken を取得し、ID トークンからテナント情報を抽出します。
@app.entrypoint
def entrypoint(payload, context: RequestContext):
try:
# カスタムヘッダーからIDトークンを取得
id_token = context.request_headers.get(
"x-amzn-bedrock-agentcore-runtime-custom-idtoken", ""
)
tenant_id = get_tenant_id_from_token(id_token)
# IDトークンを使用してABACロールを引き受け、S3クライアントを取得しエージェント処理を実行
except Exception as e:
return {"error": str(e)}
これで、以下のフローが完成します。
- ユーザーが Cognito で認証し、ID トークンとアクセストークンを取得
- Pre Token Generation Lambda により、両トークンに
custom:tenant_idクレームが含まれる - AgentCore Runtime へのリクエストに、アクセストークンを
Authorizationヘッダー、ID トークンをX-Amzn-Bedrock-AgentCore-Runtime-Custom-IdTokenヘッダーとして付与 - エージェント内で ID トークンから
custom:tenant_idを抽出し、セッションタグとして付与しながら ABAC ロールへ AssumeRole を実行 - セッションタグが
PrincipalTag/custom:tenant_idとして設定される - IAM ポリシーの条件により、S3 オブジェクトのタグと PrincipalTag が照合される
- 一致した場合のみアクセスが許可される
検証してみる
では、実際にこれまで説明したステップを検証してみましょう。
1. 環境のデプロイ
まず、CDK を使用してリソースをデプロイします。
cd cdk
npm install
cdk bootstrap # 初回のみ
cdk deploy
デプロイが完了すると、以下の出力が得られるのでそれぞれテスト用のユーザーとファイルを用意していきましょう。
-
UserPoolId: Cognito User Pool の ID -
MultiTenantBucketName: マルチテナント S3 バケット名
2. テストユーザーの作成
次に Cognito にテストユーザーを作成し、custom:tenant_id 属性を設定します。
# Tenant A のユーザーを作成
aws cognito-idp admin-create-user \
--user-pool-id ${USER_POOL_ID} \
--username ${YOUR_EMAIL_ADDRESS} \
--user-attributes Name=email,Value=${YOUR_EMAIL_ADDRESS} \
Name=custom:tenant_id,Value=tenant-a \
--message-action SUPPRESS
# パスワードを設定
aws cognito-idp admin-set-user-password \
--user-pool-id ${USER_POOL_ID} \
--username ${YOUR_EMAIL_ADDRESS} \
--password ${YOUR_PASSWORD} \
--permanent
以下のように作成されていればユーザーの用意はできました。
3. テストファイルの作成
S3 バケットにテスト用のオブジェクトを配置し、適切な tenant_id タグを付与します。
# Tenant A のファイルを作成
echo "This is tenant-a data" > tenant-a-file.txt
aws s3api put-object --bucket ${MULTI_TENANT_BUCKET} --key "tenant-a/file1.txt" \
--body "tenant-a-file.txt" --tagging "tenant_id=tenant-a"
# Tenant B のファイルを作成
echo "This is tenant-b data" > tenant-b-file.txt
aws s3api put-object --bucket ${MULTI_TENANT_BUCKET} --key "tenant-b/file1.txt" \
--body "tenant-b-file.txt" --tagging "tenant_id=tenant-b"
4. AgentCore Runtime の呼び出し
取得した ID トークンとアクセストークンを使用して、AgentCore Runtime を呼び出します。Authorization ヘッダーには AgentCore Runtime の認証用にアクセストークンを、X-Amzn-Bedrock-AgentCore-Runtime-Custom-IdToken ヘッダーには ABAC のセッションタグ設定用に ID トークンを渡します。
今回はテスト呼び出し用のスクリプトを使用します。
# Bedrock Agent Runtime APIを呼び出し
curl -Ss -X POST "${BEDROCK_AGENTCORE_URL}" \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H "X-Amzn-Bedrock-AgentCore-Runtime-Custom-IdToken: ${ID_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"prompt": "'$1'"}' | jq -r .
5. 検証結果
tenant-a のデータにアクセスする場合
まずは Tenant A ユーザー(custom:tenant_id=tenant-a)で認証し、tenant-a のデータを問い合わせてみましょう。
$ bash ./scripts/run_test.sh "tenant-aフォルダのファイル一覧を表示してください"
tenant-aフォルダのファイル一覧を取得しました。以下がファイル一覧です:
1. tenant-a/file1.txt
このフォルダには現在1つのファイルのみが存在しているようです。他に何か確認したい情報やファイルはありますか?
正常にファイルを確認できたことがわかります。
tenant-b のデータにアクセスする場合
今度は Tenant A ユーザー(custom:tenant_id=tenant-a)で認証し、tenant-b のデータを問い合わせてみましょう。
$ bash ./scripts/run_test.sh "tenant-bフォルダのファイル一覧を表示してください"
申し訳ありません。再度エラーが発生しました。この状況から、以下のことが考えられます:
1. tenant-bフォルダへのアクセス権限に問題がある可能性があります。
2. システムに一時的な問題が発生している可能性があります。
この問題を解決するためには、システム管理者に連絡して、以下の点を確認する必要があるかもしれません:
1. tenant-bフォルダへのアクセス権限が正しく設定されているか
2. システムに何か問題が発生していないか
ユーザーの皆様には、しばらく時間を置いてから再度お試しいただくか、もしくはシステム管理者にお問い合わせいただくことをお勧めします。申し訳ありませんが、現時点でtenant-bフォルダの内容を直接表示することはできません。
権限と一致するフォルダ・ファイルが存在せず、エラーとなることが分かりました。
まとめ - ABAC のメリット
ABAC を用いて権限管理を行うことで以下のことが担保されます。
-
スケーラビリティ
新しいテナントを追加する際、IAM ロールやポリシーを追加する必要がなく、ユーザー属性とオブジェクトタグを設定するだけで済みます -
シンプルな権限管理
単一の IAM ロールで全テナントのアクセス制御を実現できます -
柔軟性
テナント単位だけでなく、プロジェクト単位や部署単位など、さまざまな属性ベースの制御が可能です -
セキュリティ
タグとセッションタグの照合により、確実にテナント間のデータ分離を実現できます
おわりに
いかがでしたでしょうか。Amazon AgentCore Runtime においてもマルチテナントを ABAC で実現することで拡張性を保ちながら、セキュリティを強固にできると感じています。
また、ABAC 自体は汎用的なセキュリティ対策ですので、DynamoDB など他サービスへのアクセスにも活用できると思います。
Amazon AgentCore Runtime でどのようにマルチテナントで運用していくか困っている方の参考になれば幸いです。

