Skip to content
Open
Show file tree
Hide file tree
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 Apr 21, 2026
6ef0cc7
feat: add CDK PolicyStack for AgentCore Policy Engine
ren8k Apr 21, 2026
aae9775
chore: include PolicyStack lib in advanced interceptor sample
ren8k Apr 21, 2026
94a6515
feat: add Cedar policies for Design 1 and Design 3
ren8k Apr 21, 2026
a90082d
feat: add Design 3 Request Interceptor Lambda
ren8k Apr 21, 2026
9eaef3b
feat: add pre-deploy and cdk.json generation scripts
ren8k Apr 21, 2026
a483e7e
feat: add end-to-end policy verification script
ren8k Apr 21, 2026
0e7a362
docs: add Phase 2 deployment guide for advanced Policy + Interceptor
ren8k Apr 21, 2026
266f247
docs(deployment): link Phase 2 (advanced Policy + Interceptor) from P…
ren8k Apr 21, 2026
cfad90e
docs(lakehouse-agent): add Phase 2 section linking advanced Policy + …
ren8k Apr 21, 2026
864d3fe
docs(deployment): note that each Cognito user must sign in once befor…
ren8k Apr 21, 2026
fd26a69
docs: add Renya Kujirada to CONTRIBUTORS.md
ren8k Apr 22, 2026
6ea19f4
fix(policy-gateway-interceptor): enforce https scheme for JWKS fetch
ren8k Apr 24, 2026
d1733fb
fix(policy-gateway-interceptor): allow overriding TEST_PASSWORD via e…
ren8k Apr 24, 2026
f407544
chore(policy-gateway-interceptor): bump aws-cdk-lib to 2.250.0
ren8k Apr 24, 2026
d1ee3ae
style(policy-gateway-interceptor): drop unused sys import
ren8k Apr 24, 2026
68502dc
Merge branch 'main' into feature/advanced-agentcore-policy-gateway-in…
ren8k Apr 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ lib/
!04-infrastructure-as-code/cdk/typescript/knowledge-base-rag-agent/infrastructure/lib/
!01-tutorials/01-AgentCore-runtime/01-hosting-agent/05-java-agents/01-springai-with-bedrock-model/infra/lib/
!01-tutorials/01-AgentCore-runtime/01-hosting-agent/05-java-agents/02-embabel-with-bedrock-model/infra/lib/
!02-use-cases/lakehouse-agent/deployment/advanced-agentcore-policy-gateway-interceptor/lib/
lib64/
parts/
sdist/
Expand Down
22 changes: 22 additions & 0 deletions 02-use-cases/lakehouse-agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,28 @@ See [deployment/README.md](deployment/README.md) for full details including Lake

---

## Optional: Advanced AgentCore Policy + Lambda Interceptors (Phase 2)

The base deployment above (Option A notebooks or Option B CLI) stands alone.
Once it is working, you can optionally layer Cedar-based AgentCore Policy and a
Design 3 Request Interceptor on top. This is the companion sample for the blog
post *"Build Secure AI Agent Behavior with Policy and Lambda Interceptors in
Amazon Bedrock AgentCore"* and demonstrates three patterns:

- **Design 1 — Policy Only**: a declarative Cedar `forbid` rule denies
`get_claims_summary` for policyholders.
- **Design 2 — Interceptor Only**: the request Interceptor exchanges the JWT
for tenant-scoped IAM credentials via `sts:AssumeRole`, so Lake Formation
transparently enforces row- and column-level security per user.
- **Design 3 — Policy + Interceptor**: the Interceptor injects user geography
and Cedar evaluates `context.input.geography` to block EU users from
individual-claim tools.

Deployment, verification, and cleanup steps are in
[deployment/advanced-agentcore-policy-gateway-interceptor/README.md](deployment/advanced-agentcore-policy-gateway-interceptor/README.md).

---

## What Gets Deployed

- **Cognito User Pool**: OAuth authentication with test users and groups
Expand Down
66 changes: 62 additions & 4 deletions 02-use-cases/lakehouse-agent/deployment/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

This guide provides the deployment sequence for the Lakehouse Agent system using command-line scripts. For a guided notebook-based approach, see the Jupyter notebooks in the parent directory.

The deployment is organized in two phases:

- **Phase 1 — Base lakehouse-agent (Steps 1–7, this guide).** Deploys Cognito,
IAM tenant roles, S3 Tables + Lake Formation, the MCP server, the Gateway
with request/response Interceptors, and the conversational agent.
- **Phase 2 — Advanced AgentCore Policy + Interceptor (optional, CDK).** Layers
Cedar-based AgentCore Policy on top of Phase 1 and upgrades the request
Interceptor with geography-based access control. See
[advanced-agentcore-policy-gateway-interceptor/README.md](advanced-agentcore-policy-gateway-interceptor/README.md).

## Prerequisites

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Architcture diagram missing

1. AWS CLI configured with appropriate permissions
Expand Down Expand Up @@ -72,6 +82,8 @@ Test users created:

Default password: `TempPass123!`

> **Important — first-time sign-in required.** `setup_cognito.py` creates users with the default password as a *temporary* password, so every user starts in Cognito `FORCE_CHANGE_PASSWORD` state. `admin_initiate_auth` returns a `NEW_PASSWORD_REQUIRED` challenge (not an `AuthenticationResult`) until each user signs in once and completes the challenge. The Streamlit UI (Step 8) has a built-in challenge handler — launch it and sign in once per user, setting the new password to the same `TempPass123!` (the user pool does not configure `PasswordHistorySize`, so reusing the value is allowed). Only after this step will plain `admin_initiate_auth` calls (for example, from `verify_policy.py` in the Phase 2 sample) succeed.

#### Optional: Enable Login Audit Logging

To enable login audit logging, deploy the Post-Authentication Lambda before running setup_cognito.py:
Expand Down Expand Up @@ -302,6 +314,26 @@ Access at: http://localhost:8501

---

### Step 9 (Optional): Layer AgentCore Policy + Design 3 Interceptor (Phase 2)

To add declarative Cedar-based access control and geography-aware request
enrichment on top of the Phase 1 Gateway, follow
[advanced-agentcore-policy-gateway-interceptor/README.md](advanced-agentcore-policy-gateway-interceptor/README.md).

This Phase 2 deployment adds:

- `CfnPolicyEngine` with four Cedar policies (`permit_all` + three `forbid` rules).
- An IAM inline policy granting the existing Gateway role policy-evaluation permissions.
- A single `UpdateGateway` call that re-attaches both Interceptors together with
the Policy Engine in `ENFORCE` mode.
- An upgraded request Interceptor Lambda that injects user geography so Cedar
can enforce data-residency rules (Design 3).

Prerequisite: Phase 1 Steps 1–7 must be deployed first — the CDK stack reads
every ARN / ID it needs from SSM parameters populated by those steps.

---

## Quick Reference

| Step | Directory | Command |
Expand All @@ -319,6 +351,7 @@ Access at: http://localhost:8501
| 6 | `5-gateway-setup` | `python create_gateway.py --yes` |
| 7 | `6-lakehouse-agent` | `python deploy_lakehouse_agent.py --yes` |
| 8 | `streamlit-ui` | `streamlit run streamlit_app.py` |
| 9 (optional) | `advanced-agentcore-policy-gateway-interceptor` | `bash scripts/pre-deploy.sh && npx cdk deploy` |

---

Expand Down Expand Up @@ -355,9 +388,18 @@ deployment/
│ │ └── README.md
│ ├── create_gateway.py # Step 6
│ └── cleanup_gateway.py
└── 6-lakehouse-agent/ # Step 7
├── deploy_lakehouse_agent.py
└── cleanup_agent.py
├── 6-lakehouse-agent/ # Step 7
│ ├── deploy_lakehouse_agent.py
│ └── cleanup_agent.py
└── advanced-agentcore-policy-gateway-interceptor/ # Step 9 (optional, Phase 2)
├── README.md
├── bin/app.ts
├── lib/policy-stack.ts
├── policies/ # Cedar policies (Design 1 + Design 3)
├── lambda/interceptor-request/ # Design 3 Lambda source
├── scripts/ # pre-deploy + cdk.json generation
└── verification/
└── verify_policy.py
```

---
Expand All @@ -378,7 +420,23 @@ aws ssm get-parameters-by-path \

## Cleanup

Each deployment step has a dedicated cleanup script. Run them in reverse order:
Each deployment step has a dedicated cleanup script. Run them in reverse order.

**If you deployed Phase 2 (Step 9), destroy it first** — it depends on the
Phase 1 Gateway and the Gateway role, so Phase 1 cleanup will fail while the
Policy Engine is still attached.

```bash
# Step 9 (Phase 2): Destroy Policy Engine + Cedar policies + role inline policy.
# Interceptors remain attached; the CDK stack only added the Policy Engine.
cd advanced-agentcore-policy-gateway-interceptor
npx cdk destroy --force
cd ..
```

See [advanced-agentcore-policy-gateway-interceptor/README.md#cleanup](advanced-agentcore-policy-gateway-interceptor/README.md#cleanup) for notes on rolling back the Design 3 Lambda source before Phase 1 cleanup.

Then run the Phase 1 cleanup scripts:

```bash
# Step 7: Delete Lakehouse Agent
Expand Down
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
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` |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The 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)
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,
},
});
Loading
Loading