-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add advanced Policy + Lambda Interceptor sample to lakehouse-agent #1389
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ren8k
wants to merge
17
commits into
awslabs:main
Choose a base branch
from
ren8k:feature/advanced-agentcore-policy-gateway-interceptor
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 12 commits
Commits
Show all changes
17 commits
Select commit
Hold shift + click to select a range
6f9a643
chore: add CDK project skeleton for advanced AgentCore Policy + Lambd…
ren8k 6ef0cc7
feat: add CDK PolicyStack for AgentCore Policy Engine
ren8k aae9775
chore: include PolicyStack lib in advanced interceptor sample
ren8k 94a6515
feat: add Cedar policies for Design 1 and Design 3
ren8k a90082d
feat: add Design 3 Request Interceptor Lambda
ren8k 9eaef3b
feat: add pre-deploy and cdk.json generation scripts
ren8k a483e7e
feat: add end-to-end policy verification script
ren8k 0e7a362
docs: add Phase 2 deployment guide for advanced Policy + Interceptor
ren8k 266f247
docs(deployment): link Phase 2 (advanced Policy + Interceptor) from P…
ren8k cfad90e
docs(lakehouse-agent): add Phase 2 section linking advanced Policy + …
ren8k 864d3fe
docs(deployment): note that each Cognito user must sign in once befor…
ren8k fd26a69
docs: add Renya Kujirada to CONTRIBUTORS.md
ren8k 6ea19f4
fix(policy-gateway-interceptor): enforce https scheme for JWKS fetch
ren8k d1733fb
fix(policy-gateway-interceptor): allow overriding TEST_PASSWORD via e…
ren8k f407544
chore(policy-gateway-interceptor): bump aws-cdk-lib to 2.250.0
ren8k d1ee3ae
style(policy-gateway-interceptor): drop unused sys import
ren8k 68502dc
Merge branch 'main' into feature/advanced-agentcore-policy-gateway-in…
ren8k File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 9 additions & 0 deletions
9
...cases/lakehouse-agent/deployment/advanced-agentcore-policy-gateway-interceptor/.gitignore
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| node_modules/ | ||
| cdk.out/ | ||
| cdk.json | ||
| dist/ | ||
| *.d.ts | ||
| *.js.map | ||
| .ruff_cache/ | ||
| __pycache__/ | ||
| *.pyc |
207 changes: 207 additions & 0 deletions
207
...ehouse-agent/deployment/advanced-agentcore-policy-gateway-interceptor/README.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,207 @@ | ||
| # Advanced AgentCore Policy + Lambda Interceptor (CDK) | ||
|
|
||
| This CDK project extends the lakehouse-agent sample with a layered security | ||
| architecture that combines **Cedar-based AgentCore Policy** and a **Design 3 | ||
| Request Interceptor Lambda**. It implements the three patterns described in | ||
| the blog post *"Build Secure AI Agent Behavior with Policy and Lambda | ||
| Interceptors in Amazon Bedrock AgentCore"*: | ||
|
|
||
| | Design | Mechanism | Demo rule | | ||
| |---|---|---| | ||
| | **Design 1 — Policy Only** | Cedar `forbid` rule on the Gateway | Policyholders cannot invoke `get_claims_summary` | | ||
| | **Design 2 — Interceptor Only** | Request Interceptor performs `sts:AssumeRole` to scope credentials, so Lake Formation applies row- and column-level security | Each user sees only their own rows and permitted columns | | ||
| | **Design 3 — Policy + Interceptor** | Interceptor injects `geography`, Cedar evaluates `context.input.geography` | EU users cannot invoke `query_claims` or `get_claim_details` | | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ren8k Can we add a architecture diagram here |
||
| ## Prerequisites | ||
|
|
||
| This CDK sample runs **after the base lakehouse-agent is deployed** (Steps 1–7 | ||
| in [deployment/README.md](../README.md)). It reads every input — Gateway ARN, | ||
| interceptor Lambda ARNs, Cognito IDs, etc. — from the SSM parameters produced | ||
| by those steps. | ||
|
|
||
| Required tooling: | ||
|
|
||
| - AWS credentials with permissions to create AgentCore Policy Engine and | ||
| Cedar policies (`bedrock-agentcore:*`), update the Gateway, and attach IAM | ||
| inline policies to the Gateway role. | ||
| - AWS CLI v2 configured for the same account and region as the base deployment. | ||
| - Node.js 18+ and npm. | ||
| - Python 3.10+ (for the pre-deploy and verification scripts) with the same | ||
| virtual environment used for Phase 1. | ||
|
|
||
| > **Region note**: All commands below assume `AWS_REGION=us-east-1`, matching | ||
| > the base lakehouse-agent deployment. Export `AWS_REGION` before running any | ||
| > step if your shell default differs. | ||
|
|
||
| ## Directory layout | ||
|
|
||
| ``` | ||
| advanced-agentcore-policy-gateway-interceptor/ | ||
| ├── README.md # (this file) | ||
| ├── package.json / tsconfig.json # CDK TypeScript project | ||
| ├── cdk.json.example # Template — cdk.json is generated at deploy-time | ||
| ├── bin/app.ts # CDK entry point (reads account/region from context) | ||
| ├── lib/policy-stack.ts # PolicyStack: Policy Engine + Cedar policies + Gateway re-attach | ||
| ├── policies/ # Cedar source (one file per policy) | ||
| ├── lambda/interceptor-request/ # Design 3 Request Interceptor Lambda source | ||
| ├── scripts/ | ||
| │ ├── pre-deploy.sh # Runs the 3 steps below in one go | ||
| │ ├── generate-cdk-context.sh # Generates cdk.json from SSM | ||
| │ └── detach-interceptors.py # Detaches Interceptors before Cedar policy creation | ||
| └── verification/ | ||
| └── verify_policy.py # 13-check FGAC regression suite | ||
| ``` | ||
|
|
||
| ## Deploy | ||
|
|
||
| ### Step 1 — Pre-deploy | ||
|
|
||
| `pre-deploy.sh` does three things in sequence: | ||
|
|
||
| 1. **Generate `cdk.json`** from SSM Parameter Store (account ID is derived | ||
| from `aws sts get-caller-identity`). | ||
| 2. **Detach the Interceptors** from the Gateway. Cedar policy creation sends | ||
| internal MCP validation requests that are SigV4-signed (not Bearer-token | ||
| authenticated), which fail against a JWT-validating Interceptor. CDK | ||
| re-attaches both Interceptors together with the Policy Engine in Step 2. | ||
| 3. **Overwrite the base `interceptor-request/lambda_function.py`** with the | ||
| Design 3 version (adds user geography injection) and redeploy that Lambda. | ||
|
|
||
| ```bash | ||
| cd 02-use-cases/lakehouse-agent/deployment/advanced-agentcore-policy-gateway-interceptor | ||
| AWS_REGION=us-east-1 bash scripts/pre-deploy.sh | ||
| ``` | ||
|
|
||
| ### Step 2 — CDK deploy | ||
|
|
||
| ```bash | ||
| npm ci | ||
| # Bootstrap once per account/region if you have not deployed any CDK stack yet: | ||
| # npx cdk bootstrap | ||
| npx cdk deploy --require-approval never | ||
| ``` | ||
|
|
||
| This creates: | ||
|
|
||
| - **`CfnPolicyEngine`** — the AgentCore Policy Engine. | ||
| - **`CfnPolicy` x 4** — Cedar policies from `policies/*.cedar`. `permit_all` | ||
| is created first (with `IGNORE_ALL_FINDINGS` to bypass the Overly Permissive | ||
| warning), then the three `forbid` policies in parallel. | ||
| - **IAM inline policy** on the existing Gateway role for | ||
| `bedrock-agentcore:AuthorizeAction` etc. | ||
| - **`AwsCustomResource` → `UpdateGateway`** — re-attaches both Interceptors | ||
| and attaches the Policy Engine in `ENFORCE` mode in a single API call. | ||
|
|
||
| Deployment takes about 2 minutes. | ||
|
|
||
| ### Step 3 — Verify the Policy Engine is active | ||
|
|
||
| ```bash | ||
| AWS_REGION=us-east-1 python3 -c " | ||
| import boto3 | ||
| client = boto3.Session(region_name='us-east-1').client('bedrock-agentcore-control') | ||
| for e in client.list_policy_engines().get('policyEngines', []): | ||
| if 'Lakehouse' in e['name']: | ||
| print(f'Engine: {e[\"policyEngineId\"]} ({e[\"status\"]})') | ||
| for p in client.list_policies(policyEngineId=e['policyEngineId']).get('policies', []): | ||
| print(f' {p[\"name\"]}: {p[\"status\"]}') | ||
| " | ||
| ``` | ||
|
|
||
| All four policies should report `ACTIVE`. | ||
|
|
||
| > **Before running Step 4: each Cognito user must sign in once.** `verify_policy.py` authenticates via plain `admin_initiate_auth`, which fails while users are still in Cognito `FORCE_CHANGE_PASSWORD` state (the default for users created by Phase 1 `setup_cognito.py`). Start the Streamlit UI (`streamlit run streamlit-ui/streamlit_app.py`) and sign in **once per user** — `policyholder001`, `policyholder002`, `adjuster001`, `adjuster002`, and `admin` — completing the `NEW_PASSWORD_REQUIRED` challenge. Re-entering the same `TempPass123!` as the new password works because the user pool does not set `PasswordHistorySize`. After this one-time step, `verify_policy.py` will authenticate cleanly. | ||
|
|
||
| ### Step 4 — Run the end-to-end verification | ||
|
|
||
| ```bash | ||
| cd ../../.. # back to lakehouse-agent/ | ||
| source .venv/bin/activate # same venv used for Phase 1 | ||
| python deployment/advanced-agentcore-policy-gateway-interceptor/verification/verify_policy.py | ||
| ``` | ||
|
|
||
| Expected output: | ||
|
|
||
| ``` | ||
| Results: 13/13 passed | ||
| ``` | ||
|
|
||
| ## What the policies enforce | ||
|
|
||
| | Policy file | Pattern | Effect | | ||
| |---|---|---| | ||
| | `permit_all.cedar` | Baseline permit | Without this, AgentCore defaults to deny-by-default once a Policy Engine is attached | | ||
| | `forbid_policyholder_summary.cedar` | Design 1 | Blocks `get_claims_summary` when `principal.getTag("cognito:groups") like "*policyholders*"` | | ||
| | `forbid_eu_individual_claims.cedar` | Design 3 | Blocks `query_claims` and `get_claim_details` when `context.input.geography == "EU"` | | ||
| | `forbid_restricted_geography.cedar` | Design 3 | Blocks every tool when `context.input.geography == "RESTRICTED"` | | ||
|
|
||
| The `geography` attribute is injected by the Design 3 Request Interceptor at | ||
| `params.arguments.geography` (top level). Cedar maps that to | ||
| `context.input.geography`. The demo Lambda ships a hard-coded mapping in | ||
| `USER_GEOGRAPHY` — replace with a DynamoDB lookup for production. | ||
|
|
||
| ## Cleanup | ||
|
|
||
| Destroy **in reverse order**. Phase 2 first, then Phase 1. | ||
|
|
||
| ### Phase 2 — This CDK stack | ||
|
|
||
| ```bash | ||
| cd 02-use-cases/lakehouse-agent/deployment/advanced-agentcore-policy-gateway-interceptor | ||
| npx cdk destroy --force | ||
| ``` | ||
|
|
||
| `cdk destroy` does the following via the `AwsCustomResource` | ||
| and the `CfnPolicy` / `CfnPolicyEngine` resource lifecycles: | ||
|
|
||
| 1. Detaches the Policy Engine from the Gateway (Interceptors remain attached). | ||
| 2. Deletes the four Cedar policies. | ||
| 3. Deletes the Policy Engine. | ||
| 4. Removes the inline IAM policy from the Gateway role. | ||
|
|
||
| > **Note:** The Design 3 Request Interceptor Lambda source (with geography | ||
| > injection) remains deployed after `cdk destroy`. That is intentional — the | ||
| > Lambda is a Phase 1 resource and is cleaned up in the Phase 1 cleanup below. | ||
| > If you want to roll back to the original Phase 1 Lambda (without geography | ||
| > injection) before destroying Phase 1, restore | ||
| > `deployment/5-gateway-setup/interceptor-request/lambda_function.py` from git | ||
| > and redeploy: | ||
| > | ||
| > ```bash | ||
| > git checkout -- deployment/5-gateway-setup/interceptor-request/lambda_function.py | ||
| > cd deployment/5-gateway-setup/interceptor-request | ||
| > AWS_REGION=us-east-1 ./deploy.sh | ||
| > ``` | ||
|
|
||
| ### Phase 1 — Base lakehouse-agent | ||
|
|
||
| Follow the standard cleanup in the parent guide — each Phase 1 step has a | ||
| dedicated cleanup script, run in reverse order: | ||
|
|
||
| ```bash | ||
| cd 02-use-cases/lakehouse-agent/deployment | ||
| cd 6-lakehouse-agent && python cleanup_agent.py | ||
| cd ../5-gateway-setup && python cleanup_gateway.py | ||
| cd ../4-mcp-lakehouse-server && python cleanup_runtime.py | ||
| cd ../3-s3tables-setup && python cleanup_s3tables.py | ||
| cd ../2-lakehouse-tenant-roles-setup && python cleanup_iam_roles.py | ||
| cd ../1-cognito-setup && python cleanup_cognito.py | ||
| ``` | ||
|
|
||
| See [../README.md](../README.md) for details. | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| | Symptom | Cause | Fix | | ||
| |---|---|---| | ||
| | `CfnPolicy` → `CREATE_FAILED` with `InterceptorException` | The Gateway still had the JWT-validating Interceptor attached while Cedar tried its internal MCP validation | Re-run `scripts/pre-deploy.sh` (it detaches Interceptors) then `cdk deploy` again | | ||
| | All tool calls return 500 | After detach, the Response Interceptor Lambda was missing when CDK re-attached | Deploy the Response Interceptor first: `deployment/5-gateway-setup/interceptor-response/deploy.sh` | | ||
| | `permit_all` fails with "Overly Permissive" | `validationMode: FAIL_ON_ANY_FINDINGS` on a broad permit | PolicyStack already uses `IGNORE_ALL_FINDINGS` for `permit_all` — rerun `cdk deploy` | | ||
| | `context.input` returns `attribute not found` | Cedar rule used a wildcard `action` | List tools explicitly in `action in [...]` (see `forbid_eu_individual_claims.cedar`) | | ||
| | Every tool returns DENY after deploy | `permit_all` is not `ACTIVE` | Re-check `list_policies` status; if not ACTIVE, re-run `cdk deploy` | | ||
|
|
||
| ## References | ||
|
|
||
| - Blog post: *Build Secure AI Agent Behavior with Policy and Lambda Interceptors in Amazon Bedrock AgentCore* | ||
| - [Phase 1 deployment guide](../README.md) | ||
| - [lakehouse-agent README](../../README.md) | ||
20 changes: 20 additions & 0 deletions
20
...cases/lakehouse-agent/deployment/advanced-agentcore-policy-gateway-interceptor/bin/app.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| #!/usr/bin/env node | ||
| import * as cdk from "aws-cdk-lib"; | ||
| import { PolicyStack } from "../lib/policy-stack"; | ||
|
|
||
| const app = new cdk.App(); | ||
|
|
||
| const account = | ||
| (app.node.tryGetContext("account") as string | undefined) ?? | ||
| process.env.CDK_DEFAULT_ACCOUNT; | ||
| const region = | ||
| (app.node.tryGetContext("region") as string | undefined) ?? | ||
| process.env.CDK_DEFAULT_REGION ?? | ||
| "us-east-1"; | ||
|
|
||
| new PolicyStack(app, "LakehousePolicyStack", { | ||
| env: { | ||
| account, | ||
| region, | ||
| }, | ||
| }); |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Architcture diagram missing