Amazon Bedrock AgentCore Policy が GA したので Cedar による MCP ツールのアクセス制御を検証してみた

記事タイトルとURLをコピーする

【2026年3月12日 訂正】AgentCore Policy は IAM 認証および認証なしの Gateway では現時点でサポートされていないことを確認しました(対応は優先的に進行中とのこと)。

本記事の公開時点では、IAM 認証ゲートウェイでも Cedar ポリシーによるアクション制御が動作するという検証結果を掲載していました。しかし、公開後に別の IAM 認証ゲートウェイで再検証したところポリシーが正常に動作しなかったため、AWS サポートに問い合わせました。その結果、AgentCore Policy は IAM 認証および認証なしの Gateway では現時点でサポートされていないことが確認されました(対応は優先的に進行中とのこと)。本記事の IAM 認証パターンの検証結果およびまとめを訂正しています。Cedar ポリシーによるアクセス制御を行う場合は、OAuth 認証のゲートウェイを使用してください。

なお、公式ドキュメントの Limitations には以下の記載があります。

After IAM authorization succeeds, Cedar policies can be used to enforce fine-grained constraints on actions, resources, tool inputs, and contextual attributes.

ドキュメント上は IAM 認証でも action や resource に対する Cedar ポリシーの制約が可能と読めますが、AWS サポートに確認したところ、この記載は将来の実装を先行して記述したものであり、現時点では未実装とのことでした。IAM 認証 Gateway での Policy サポートは優先的に対応中とのことです。

はじめに

こんにちは。アプリケーションサービス部エデュケーショナルサービス課の山本です。
部の名前は「アプリ」ですが、インフラエンジニア(?)として中途採用者の教育などを行っています。
「見たことない魔物」のコール動画を聴きながらランニング(ファルトレク)するのが最近の趣味です。

2026年3月3日、ひな祭りの日にPolicy in Amazon Bedrock AgentCore が GA しました

Policy は、AgentCore Gateway 上でツールへのアクセスを制御する認可の仕組みです。エージェントのコードを変更することなく、Cedar ポリシーを定義するだけで「誰が」「どのツールを」使えるかを管理できます。自然言語からの Cedar 自動変換にも対応しています。

本記事は、以下の2つの記事の続編です。

前回までで「Cognito 認証(OAuth)」と「IAM 認証(SigV4)」の2つの接続パターンが揃いました。今回は、これらの認証方式の違いが Cedar ポリシーによるアクセス制御にどう影響するのかを検証します。

Policy は AWS マネジメントコンソールでは以下の場所にあります。※現時点での情報です。

ゲートウェイの作成時や更新時にも関連づけることができます。作成時の画面での Policy の設定箇所です。

Cedar ポリシーの基本

Cedar は AWS が開発したオープンソースの認可ポリシー言語です。AgentCore Gateway では、「誰が」「どのツールを」「どのゲートウェイで」使えるかを制御できます。

permit (
    principal is AgentCore::OAuthUser,  // 誰が
    action == AgentCore::Action::"ToolName__operation",  // どのツールを
    resource == AgentCore::Gateway::"<gateway-arn>"  // どのゲートウェイで
);

when 句を追加することで、OAuth トークンに含まれる情報(scope, client_id, role 等)やツールに渡される引数の値に基づいた、より細かい条件付きの制御も可能です。

公式ドキュメントのポリシー例では、保険管理システムを題材にさまざまなパターンが紹介されています。いくつか抜粋します。

複数アクション指定 — 保険証券の参照とクレームステータスの確認だけを許可し、それ以外を拒否:

permit(
    principal is AgentCore::OAuthUser,
    action in [
        AgentCore::Action::"InsuranceAPI__get_policy",
        AgentCore::Action::"InsuranceAPI__get_claim_status"
    ],
    resource == AgentCore::Gateway::"<gateway-arn>"
);

forbid + unless — 補償内容の更新を senior-adjustermanager ロールだけに許可:

forbid(
    principal is AgentCore::OAuthUser,
    action == AgentCore::Action::"InsuranceAPI__update_coverage",
    resource == AgentCore::Gateway::"<gateway-arn>"
)
unless {
    principal.hasTag("role") &&
    (principal.getTag("role") == "senior-adjuster" || principal.getTag("role") == "manager")
};

引数の値を検証claimTypehealth, property, auto のいずれかである場合のみ許可:

permit(
    principal is AgentCore::OAuthUser,
    action == AgentCore::Action::"InsuranceAPI__file_claim",
    resource == AgentCore::Gateway::"<gateway-arn>"
)
when {
    context.input has claimType &&
    (context.input.claimType == "health" || 
     context.input.claimType == "property" || 
     context.input.claimType == "auto")
};

アクション名のフォーマット

Cedar ポリシーの action に指定するアクション名には、2つの粒度があります。

ターゲット単位での指定:

action in [AgentCore::Action::"target-quick-start-ukt8vw"]

ターゲット配下のツールをまとめて許可できます。公式ドキュメントでは Gateway Target(Action Group)と呼ばれています。

ツール単位での指定:

action in [AgentCore::Action::"target-quick-start-ukt8vw___get_alb_logs2"]

ターゲット名とツール名を ___(アンダースコア3つ)で連結した形式です。特定のツールだけを許可したい場合に使います。

なお、公式ドキュメントの例では RefundTool__process_refund(アンダースコア2つ)と記載されていますが、MCP クライアントのツール一覧に表示されるツール名はアンダースコア3つ(target-quick-start-ukt8vw___get_alb_logs2)でした。

実際に2つで指定するとバリデーションエラーになります。

存在しないアクション名を指定した場合も同様です。


パターン1: IAM 認証(SigV4)での検証(2026/3/12 時点でサポートされていない)

【訂正】 AWS サポートへの問い合わせの結果、AgentCore Policy は IAM 認証および認証なしの Gateway では現時点でサポートされていないことが確認されました。以下の検証1〜3は、本記事の初回公開時に特定のゲートウェイで動作した結果を記載していますが、再現性がなく、現在は正常に動作しません。参考情報として残しますが、IAM 認証ゲートウェイで Cedar ポリシーを使用しないでください。

構成

MCP Proxy for AWS を使い、SigV4 認証でゲートウェイに接続するパターンです。

flowchart LR
    subgraph Client["MCP クライアント"]
        Kiro["Kiro"]
    end

    subgraph Proxy["MCP Proxy for AWS"]
        SigV4["SigV4 署名"]
    end

    subgraph AgentCore["Amazon Bedrock AgentCore"]
        GW["AgentCore Gateway"]
        PE["Policy Engine\n(Cedar)"]
        GW -- "認可リクエスト" --> PE
        PE -- "許可 / 拒否" --> GW
        subgraph Targets["ゲートウェイターゲット"]
            T1["get_alb_logs1"]
            T2["get_alb_logs2"]
        end
        GW -- "許可された場合" --> Targets
    end

    Kiro -- "MCP プロトコル" --> SigV4
    SigV4 -- "SigV4 認証" --> GW

    style PE fill:#f9a825,stroke:#f57f17,color:#000
    style GW fill:#1565c0,stroke:#0d47a1,color:#fff
    style T1 fill:#e0e0e0,stroke:#9e9e9e,color:#000
    style T2 fill:#43a047,stroke:#2e7d32,color:#fff

MCP クライアントからの接続設定は以下の通りです。

{
  "gateway-quick-start": {
    "command": "uvx",
    "args": [
      "mcp-proxy-for-aws@latest",
      "https://gateway-quick-start-xxxxxx-xxxxxxxxxx.gateway.bedrock-agentcore.ap-northeast-1.amazonaws.com/mcp",
      "--service",
      "bedrock-agentcore",
      "--region",
      "ap-northeast-1"
    ],
    "env": {
      "AWS_PROFILE": "default"
    }
  }
}

ポリシーを使用しない場合、ツールが 2つ見えている状態になります。

  1. get_alb_logs1
  2. get_alb_logs2

検証1: すべてのアクションを許可するポリシー

まず、すべてのアクションを許可するポリシーを試みました。

permit (
    principal is AgentCore::OAuthUser,
    action,
    resource ==
        AgentCore::Gateway::"arn:aws:bedrock-agentcore:ap-northeast-1:123456789012:gateway/gateway-quick-start-eba965-os0pacz6in"
);

しかし、このポリシーは「Overly Permissive」として警告が表示されました。

Overly Permissive: Policy Engine will allow every request for the specified principal (AgentCore::OAuthUser), action (Any Future Tools) and resource (gateway/*) combination if the policy is added or updated

action を制限しないポリシーは、将来追加されるツールも含めてすべて許可してしまうため、Policy Engine が過剰な権限として警告します。実運用では、許可するアクションを明示的に指定する必要があります。

検証2: 特定のアクションだけ許可するポリシー

get_alb_logs2 だけを許可するポリシーに変更しました。

permit (
    principal is AgentCore::OAuthUser,
    action in [AgentCore::Action::"target-quick-start-7pyx00___get_alb_logs2"],
    resource ==
        AgentCore::Gateway::"arn:aws:bedrock-agentcore:ap-northeast-1:123456789012:gateway/gateway-quick-start-eba965-os0pacz6in"
);

結果、MCP クライアントのツール一覧には get_alb_logs2 だけが表示され、get_alb_logs1 は表示されなくなりました

公式ドキュメントには以下の記述があります。

Tool listing is treated as a meta action. A principal is only allowed to see tools in the listing that they would be permitted to call by policy.

ツール一覧(tools/list)の時点で Cedar ポリシーが評価され、許可されたツールだけがクライアントに返されます。ポリシーで許可されていないツールはそもそも見えないため、エージェントが誤って呼び出すこともありません。

検証3: OAuth クレームによる条件付きポリシー

OAuth クレームを使った条件付きポリシーも試しました。意図的に存在しない client_id を設定しています。

permit (
    principal is AgentCore::OAuthUser,
    action in [AgentCore::Action::"target-quick-start-7pyx00___get_alb_logs2"],
    resource ==
        AgentCore::Gateway::"arn:aws:bedrock-agentcore:ap-northeast-1:123456789012:gateway/gateway-quick-start-eba965-os0pacz6in"
) when {
    principal.hasTag("client_id") &&
    principal.getTag("client_id") == "xxxxxxxxxxxxxxxxxxxxxxxxxx123"
};

結果、MCP クライアントのツール一覧は No tools available となりました。

MCP Proxy for AWS は SigV4(IAM 認証)でゲートウェイに接続するため、JWT トークンが存在しません。JWT がなければ client_id タグもマッピングされないため、when 条件が満たされず、ポリシーが適用されません。Cedar はデフォルト拒否なので、適用されるポリシーがなければすべてのアクセスが拒否されます。

IAM 認証パターンのまとめ

【訂正】 AWS サポートに確認したところ、IAM 認証および認証なしの Gateway では AgentCore Policy は現時点でサポートされていません。上記の検証1〜3は初回公開時に特定のゲートウェイで動作した結果ですが、別のゲートウェイでは再現せず、以下のような問題が発生しました。

  • ポリシー作成時に unable to find an applicable action given the policy scope constraints エラーが発生
  • Policy engine を Remove → 再アタッチするとバリデーションは通るが、ENFORCE モードにするとツールが 0 件になる(MONITOR モードでは正常に 2 tools 返される)
制御の種類 SigV4 認証での動作
アクション(ツール名)による制御 未サポート
リソース(ゲートウェイ ARN)による制御 未サポート
OAuth クレーム(scope, client_id 等)による条件 未サポート

Cedar ポリシーによるアクセス制御を行う場合は、OAuth 認証のゲートウェイを使用してください。


パターン2: OAuth 認証(Cognito)での検証

構成

Cognito の Client Credentials フローで OAuth トークンを取得し、ゲートウェイに接続するパターンです。Kiro と AgentCore Gateway の間に Python で書いた Bridge サーバーを挟んでいます。

flowchart LR
    subgraph Client["MCP クライアント"]
        Kiro["Kiro"]
    end

    subgraph Bridge["Bridge サーバー(Python)"]
        FastMCP["FastMCP"]
    end

    subgraph Cognito["Amazon Cognito"]
        Token["OAuth Token\n(Client Credentials)"]
    end

    subgraph AgentCore["Amazon Bedrock AgentCore"]
        GW["AgentCore Gateway"]
        PE["Policy Engine\n(Cedar)"]
        GW -- "認可リクエスト" --> PE
        PE -- "クレームチェック" --> GW
        subgraph Targets["ゲートウェイターゲット"]
            T1["get_alb_logs1"]
            T2["get_alb_logs2"]
        end
        GW -- "許可された場合" --> Targets
    end

    Kiro -- "MCP プロトコル" --> FastMCP
    FastMCP -- "トークン取得" --> Token
    FastMCP -- "Bearer Token" --> GW

    style PE fill:#f9a825,stroke:#f57f17,color:#000
    style GW fill:#1565c0,stroke:#0d47a1,color:#fff
    style Token fill:#7b1fa2,stroke:#4a148c,color:#fff

Bridge サーバーは起動時に Cognito の Client Credentials フローでアクセストークン(JWT)を取得し、Authorization: Bearer <token> ヘッダー付きでゲートウェイに接続します。

ゲートウェイは受け取った JWT を解析し、含まれるクレーム(client_id, scope 等)を Cedar ポリシーの principal タグとして利用できるようにします。これにより、Cedar ポリシーの when 条件で principal.getTag("client_id")principal.getTag("scope") のようにクレームの値を参照し、アクセスの許可・拒否を判定できます。

ゲートウェイのサービスロールにポリシーエンジン関連の権限が必要

OAuth 認証のゲートウェイにポリシーエンジンをアタッチした際、以下のエラーが発生しました。

InternalServerException - Policy evaluation failed for list tools

これはゲートウェイのサービスロールに Cedar ポリシー評価用の IAM 権限が不足していたためです。パターン1の SigV4 認証ゲートウェイは Starter Toolkit で作成しており、bedrock-agentcore:* のワイルドカード権限が最初から付与されていたため、この問題に気づきませんでした。

公式ドキュメントにも以下の記載があります。

Without these permissions, the Gateway cannot perform policy authorization. Attaching a Policy Engine to an existing Gateway will result in an InternalServerException.

実行ロールに以下の3つの権限を追加することで解消しました。

  • bedrock-agentcore:AuthorizeAction
  • bedrock-agentcore:PartiallyAuthorizeActions
  • bedrock-agentcore:GetPolicyEngine

Starter Toolkit で作成したゲートウェイは bedrock-agentcore:* のワイルドカード権限が付与されているため問題になりませんが、カスタムで作成したゲートウェイでは明示的に追加する必要があります。最小権限を付与するのが実運用では正しいです。

検証4: client_id による条件付きポリシー

JWT トークンに含まれる client_id クレーム(Cognito のアプリケーションクライアント ID)を使ったアクセス制御を検証しました。

正しい client_id での検証(許可)

permit (
    principal is AgentCore::OAuthUser,
    action in [
        AgentCore::Action::"target-quick-start-ax6fgk___get_alb_logs1",
        AgentCore::Action::"target-quick-start-ukt8vw___get_alb_logs2"
    ],
    resource ==
        AgentCore::Gateway::"arn:aws:bedrock-agentcore:ap-northeast-1:123456789012:gateway/alb-log-gateway-h8zvrcmytf"
) when {
    principal.hasTag("client_id") &&
    principal.getTag("client_id") == "xxxxxxxxxxxxxxxxxxxxxxxxxx"
};

結果、2つのツールが正しく表示されました。

間違った client_id での検証(拒否)

client_id"qq" に変更したところ、Found 0 tools となりツールが一切返されませんでした。

Cognito のアプリクライアント単位でゲートウェイへのアクセスを制御できることが確認できました。例えば、本番用クライアントと開発用クライアントで異なるツールへのアクセス権を設定する、といった運用が可能です。

検証5: scope による条件付きポリシー

JWT トークンに含まれる scope クレームを使ったアクセス制御を検証しました。Cognito のリソースサーバーで定義したカスタムスコープに基づいて、ツールへのアクセスを制御します。

カスタムスコープ の定義は genesis-gateway:invoke としました。

アプリケーションクライアントにカスタムスコープを割り当てました。

間違った scope での検証(拒否)

意図的に存在しない scope を指定しました。

permit (
    principal is AgentCore::OAuthUser,
    action in [
        AgentCore::Action::"target-quick-start-ax6fgk___get_alb_logs1",
        AgentCore::Action::"target-quick-start-ukt8vw___get_alb_logs2"
    ],
    resource ==
        AgentCore::Gateway::"arn:aws:bedrock-agentcore:ap-northeast-1:123456789012:gateway/alb-log-gateway-h8zvrcmytf"
) when {
    principal.hasTag("scope") &&
    principal.getTag("scope") like "*wrong-scope*"
};

結果、Found 0 tools となりツールが一切返されませんでした。

正しい scope での検証(許可)

Cognito で実際に発行されるトークンの scope genesis-gateway:invokeに合わせたポリシーに変更しました。

permit (
    principal is AgentCore::OAuthUser,
    action in [
        AgentCore::Action::"target-quick-start-ax6fgk___get_alb_logs1",
        AgentCore::Action::"target-quick-start-ukt8vw___get_alb_logs2"
    ],
    resource ==
        AgentCore::Gateway::"arn:aws:bedrock-agentcore:ap-northeast-1:123456789012:gateway/alb-log-gateway-h8zvrcmytf"
) when {
    principal.hasTag("scope") &&
    principal.getTag("scope") like "*genesis-gateway:invoke*"
};

結果、2つのツールが正しく表示されました。

scope ベースの制御は、Cognito のリソースサーバーでカスタムスコープを定義し、アプリクライアントごとに付与するスコープを変えることで実現します。

検証6: scope による複数クライアントの差分制御

検証5では単一ポリシーで scope の一致・不一致を確認しました。ここでは、複数のポリシーと複数のアプリクライアントを組み合わせて、scope の違いによってアクセスできるツールが変わることを検証します。

準備

Cognito のリソースサーバーに新しいカスタムスコープ genesis-gateway:read を追加し、新しいアプリクライアント read-only-client を作成して genesis-gateway:read だけを割り当てました。

アプリクライアント 割り当てスコープ
my-client(既存) genesis-gateway:invoke
read-only-client(新規) genesis-gateway:read

ゲートウェイのインバウンド認証設定の「許可されたクライアント」に、新しいクライアントも追加する必要があります。ここに登録されていないクライアントは、Cedar ポリシー以前にゲートウェイレベルで 401 Unauthorized になります。

ポリシー構成

Cedar では1つのポリシーに1つの permit(または forbid)文しか書けないため、ポリシーを分けて作成しました。複数の permit ポリシーは OR で評価されます。

ポリシー1: invoke スコープ → 全ツール許可

permit (
    principal is AgentCore::OAuthUser,
    action in [
        AgentCore::Action::"target-quick-start-ax6fgk___get_alb_logs1",
        AgentCore::Action::"target-quick-start-ukt8vw___get_alb_logs2"
    ],
    resource ==
        AgentCore::Gateway::"arn:aws:bedrock-agentcore:ap-northeast-1:123456789012:gateway/alb-log-gateway-h8zvrcmytf"
) when {
    principal.hasTag("scope") &&
    principal.getTag("scope") like "*genesis-gateway:invoke*"
};

ポリシー2: read スコープ → get_alb_logs1 だけ許可

permit (
    principal is AgentCore::OAuthUser,
    action == AgentCore::Action::"target-quick-start-ax6fgk___get_alb_logs1",
    resource ==
        AgentCore::Gateway::"arn:aws:bedrock-agentcore:ap-northeast-1:123456789012:gateway/alb-log-gateway-h8zvrcmytf"
) when {
    principal.hasTag("scope") &&
    principal.getTag("scope") like "*genesis-gateway:read*"
};

結果

bridge.py の接続クライアントを切り替えて検証しました。

接続クライアント スコープ 表示されたツール
my-client genesis-gateway:invoke get_alb_logs1, get_alb_logs2(2 tools)
read-only-client genesis-gateway:read get_alb_logs1(1 tool)

同じゲートウェイ・同じターゲットに対して、クライアントに割り当てられたスコープの違いだけでアクセスできるツールが変わることが確認できました。ポリシー側はスコープの値を見ているだけなので、新しいクライアントを追加する際は Cognito 側でスコープを割り当てるだけで済みます。

client_id と scope の使い分け

client_idscope はどちらも OAuth クレームベースの制御ですが、役割が違います。

client_id は「どのクライアントからのアクセスか」を判定します。ポリシーにクライアント ID を直接書くため、クライアントが増えるたびにポリシーの追加・変更が必要です。

scope は「何ができるか」を判定します。ポリシー側はスコープの値だけを見るため、クライアントが増えてもポリシーを変更する必要がありません。

scope の仕組み

scope は Cognito のリソースサーバーとアプリクライアントの組み合わせで管理します。

  1. Cognito のリソースサーバーにカスタムスコープを定義する(例: gateway/read, gateway/write
  2. アプリクライアントごとに、許可するスコープを割り当てる

例えば、同じユーザープール内に2つのアプリクライアントを作成し、それぞれ異なるスコープを割り当てます。

  • クライアントA: gateway/read のみ → 参照系ツールだけ使える
  • クライアントB: gateway/read + gateway/write → 全ツール使える

Client Credentials フローでトークンを取得すると、そのクライアントに割り当てられたスコープだけが JWT に含まれます。Cedar ポリシーでは以下のようにスコープをチェックします。

// 参照系ツール: read スコープがあれば許可
permit (
    principal is AgentCore::OAuthUser,
    action == AgentCore::Action::"ToolName__get_data",
    resource == AgentCore::Gateway::"<gateway-arn>"
) when {
    principal.hasTag("scope") &&
    principal.getTag("scope") like "*gateway:read*"
};

// 更新系ツール: write スコープがあれば許可
permit (
    principal is AgentCore::OAuthUser,
    action == AgentCore::Action::"ToolName__update_data",
    resource == AgentCore::Gateway::"<gateway-arn>"
) when {
    principal.hasTag("scope") &&
    principal.getTag("scope") like "*gateway:write*"
};

この構成であれば、新しいクライアントを追加する際は Cognito 側でスコープを割り当てるだけで済み、Cedar ポリシーの変更は不要です。

OAuth 認証パターンのまとめ

制御の種類 OAuth 認証での動作
アクション(ツール名)による制御 効く
リソース(ゲートウェイ ARN)による制御 効く
client_id による条件 効く
scope による条件 効く

OAuth 認証では、JWT トークンのクレームが Cedar の principal タグとしてマッピングされるため、client_idscope を使った細かいアクセス制御が可能です。


まとめ

AgentCore の Policy(Cedar)を IAM 認証(SigV4)と OAuth 認証(Cognito)の2パターンで検証しました。

制御の種類 IAM 認証(SigV4) OAuth 認証(Cognito)
アクション(ツール名)による制御 未サポート 効く
リソース(ゲートウェイ ARN)による制御 未サポート 効く
client_id による条件 未サポート 効く
scope による条件 未サポート 効く
  • tools/list の時点でポリシーが評価され、許可されたツールだけが返される
  • IAM 認証および認証なしのゲートウェイでは、Cedar ポリシーは現時点で未サポート(AWS サポート確認済み、対応中)
  • OAuth クレームベースの細かいアクセス制御(client_id, scope 等)が必要な場合は、Cognito 等の OAuth プロバイダーからトークンを取得してゲートウェイに接続する構成が必要
  • カスタムで作成したゲートウェイにポリシーエンジンをアタッチする場合は、実行ロールに AuthorizeAction, PartiallyAuthorizeActions, GetPolicyEngine の権限を忘れずに追加する
  • scope ベースの制御を使えば、ポリシーを変更せずに Cognito 側でクライアントごとのアクセス権を管理できる

Policy は GA したばかりの機能ですが、エージェントコードに手を入れずにツール単位のアクセス制御ができるのは運用上とても便利です。IAM 認証ゲートウェイでは Cedar ポリシーが未サポートのため、OAuth 認証のゲートウェイを使用してください。scope ベースの制御と組み合わせれば、クライアント単位のきめ細かい制御も実現できます。

余談

全社イベントで久々に東京に行きました。朝ランしていると街の風景が色々と変わっていくのが楽しかったです。
品川で見た本物のコンテナ:

山本 哲也 (記事一覧)

多分インフラエンジニアです。データ分析に興味あります。

山を走るのが趣味です。今年は 100 マイルレース完走します。