Skip to content

Commit 74600e2

Browse files
brnaba-awsbrnaba-aws
andauthored
Update demo (#1150)
* added full example of enterprise mcp platform with policy engine mcp server filtering based on user_tag, guardrail for PII data * fixed linting * fixed linting * fixing lint * fixing lint * fixinf ruff * FIXING RUFF * fixing ruff * fixed stack added missing lib files * fixing ruff * fixing ruff * Added scopes, audiences on MCP gateway Added ALB headers conditions Added ALB S3 logging Added ACG VPCe with Resource Based Policy Updated architecture diagram Updated readme Fixed interceptors * auto delete objets in s3 added dependency on VPCe * added path based routing updated cdk context remove print and use logger * fixed ruff * fixing ruff * enfore association --------- Signed-off-by: Anthony Bernabeu <bernabeu.anthony@gmail.com> Co-authored-by: brnaba-aws <brnaba@amazon.com>
1 parent 6fcd3fe commit 74600e2

10 files changed

Lines changed: 1195 additions & 378 deletions

File tree

01-tutorials/02-AgentCore-gateway/04-integration/04-enterprise-mcp-demo/README.MD

Lines changed: 89 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ This CDK infrastructure deploys a complete MCP gateway solution that enables VS
1212
- **Policy-Based Access Control** with custom Cedar policies
1313
- **Request/Response Interception** for logging and transformation
1414
- **PII Protection** using Bedrock Guardrails
15-
- **Flexible Deployment Options** (ALB or API Gateway)
1615
- **Custom Domain Support** with SSL/TLS
16+
- **ALB Access Logging** to S3 with encryption and lifecycle management
1717

1818
## Architecture
1919

@@ -25,10 +25,8 @@ This CDK infrastructure deploys a complete MCP gateway solution that enables VS
2525
- **OAuth Clients**:
2626
- VS Code Client (Authorization Code Grant with PKCE)
2727

28-
#### 2. **API Gateway Layer**
29-
Choose between two deployment options:
30-
- **Application Load Balancer (ALB)**: Production-grade with custom domains and SSL/TLS
31-
- **API Gateway HTTP API**: Serverless, cost-effective for development/testing
28+
#### 2. **Application Load Balancer**
29+
Production-grade internet-facing ALB with custom domain, SSL/TLS, WAF, and access logging.
3230

3331
#### 3. **MCP Proxy Lambda**
3432
Central component that handles:
@@ -90,12 +88,12 @@ Example Lambda functions that implement MCP tools:
9088
| Amazon Cognito | OAuth 2.0 authentication and user management |
9189
| AWS Lambda | Serverless compute for proxy, MCP servers, and policy engine |
9290
| Amazon Bedrock AgentCore | MCP gateway and protocol handling |
93-
| Application Load Balancer | Production routing with custom domains (optional) |
94-
| API Gateway HTTP API | Serverless API endpoint (optional) |
95-
| Amazon VPC | Network isolation for ALB deployment |
91+
| Application Load Balancer | Internet-facing ALB with TLS, WAF, and access logging |
92+
| Amazon VPC | Network isolation with private subnets and VPC endpoints |
9693
| AWS IAM | Identity and access management |
9794
| Amazon Route53 | DNS management for custom domains |
9895
| AWS Certificate Manager | SSL/TLS certificates |
96+
| Amazon S3 | ALB access log storage (encrypted, 90-day lifecycle) |
9997
| Bedrock Guardrails | Content filtering and PII protection |
10098

10199
## Prerequisites
@@ -116,7 +114,7 @@ Example Lambda functions that implement MCP tools:
116114
### 1. Install Dependencies
117115

118116
```bash
119-
cd enterprise-mcp-infra/cdk
117+
cd cdk
120118
npm install
121119
```
122120

@@ -130,63 +128,31 @@ cdk bootstrap aws://ACCOUNT-ID/REGION
130128

131129
Edit `cdk/cdk.context.json` to configure your deployment:
132130

133-
#### Option A: ALB Deployment with Custom Domain
134-
135131
```json
136132
{
137-
"deploymentType": "ALB",
138133
"domainName": "enterprise-mcp",
139134
"hostedZoneName": "example.com",
140135
"hostedZoneId": "Z1234567890ABC",
141136
"certificateArn": "arn:aws:acm:region:account:certificate/xxx"
142137
}
143138
```
144139

145-
#### Option B: API Gateway Deployment (Default URL)
146-
147-
```json
148-
{
149-
"deploymentType": "API_GATEWAY",
150-
"domainName": "",
151-
"hostedZoneName": "",
152-
"hostedZoneId": "",
153-
"certificateArn": ""
154-
}
155-
```
156-
157-
#### Option C: API Gateway with Custom Domain
158-
159-
```json
160-
{
161-
"deploymentType": "API_GATEWAY",
162-
"domainName": "enterprise-mcp.example.com",
163-
"hostedZoneName": "example.com",
164-
"hostedZoneId": "Z1234567890ABC",
165-
"certificateArn": "arn:aws:acm:region:account:certificate/xxx"
166-
}
167-
```
168-
169140
**Configuration Parameters:**
170141

171142
| Parameter | Description | Required | Default |
172143
|-----------|-------------|----------|---------|
173-
| `deploymentType` | Deployment type: `ALB` or `API_GATEWAY` | Yes | `ALB` |
174-
| `domainName` | Custom domain name (e.g., `enterprise-mcp` for ALB, or full domain for API Gateway) | No (API Gateway only) | `""` |
175-
| `hostedZoneName` | Route53 hosted zone name (e.g., `example.com`) | Only with custom domain | `""` |
176-
| `hostedZoneId` | Route53 hosted zone ID (e.g., `Z1234567890ABC`) | Only with custom domain | `""` |
177-
| `certificateArn` | ACM certificate ARN for HTTPS | Only with custom domain | `""` |
144+
| `domainName` | Custom domain name (e.g., `enterprise-mcp`) | Yes | `""` |
145+
| `hostedZoneName` | Route53 hosted zone name (e.g., `example.com`) | Yes | `""` |
146+
| `hostedZoneId` | Route53 hosted zone ID (e.g., `Z1234567890ABC`) | Yes | `""` |
147+
| `certificateArn` | ACM certificate ARN for HTTPS | Yes | `""` |
178148

179149
### 4. Deploy the Stack
180150

181151
```bash
182152
cdk deploy
183153
```
184154

185-
You can also override context values via command line:
186-
187-
```bash
188-
cdk deploy -c deploymentType=API_GATEWAY
189-
```
155+
> **Note:** The stack is pinned to `us-east-1` in `cdk/bin/enterprise-mcp-infra.ts`. Update the `region` value there if you need a different region.
190156
191157
### 5. Save CDK Outputs
192158

@@ -208,25 +174,26 @@ EnterpriseMcpInfraStack.Gateway = agentcore-mcp-gateway-xxxxx
208174

209175
#### Using the Automated Script (Recommended)
210176

211-
1. **Edit** `enterprise-mcp-infra/scripts/script.py`:
177+
1. **Edit** `scripts/script.py`:
212178
- Replace the `output` variable content with your actual CDK outputs (from step 4)
213179
- Customize the users list with your desired email addresses and passwords
214180

215181
2. **Run the script** to create users:
216182
```bash
217-
cd enterprise-mcp-infra/scripts
183+
cd scripts
218184
python script.py
219185
```
220186

221187
The script will:
222188
- Parse the CDK outputs to extract the User Pool ID
223-
- Create two default users: `vscode-admin@example.com` and `vscode-user@example.com`
189+
- Create three default users (admin, regular, and read-only)
224190
- Set permanent passwords (no need for password reset on first login)
225191
- Skip users that already exist
226192

227193
**Default Users Created:**
228194
- `vscode-admin@example.com` / `TempPassword123!`
229195
- `vscode-user@example.com` / `TempPassword1234!`
196+
- `vscode-readonly@example.com` / `TempPassword1235!`
230197

231198
#### Manual User Creation (Alternative)
232199

@@ -374,15 +341,26 @@ cdk deploy
374341

375342
### Lambda Logs
376343

377-
```bash
378-
# MCP Proxy Lambda
379-
aws logs tail /aws/lambda/<ProxyLambdaName> --follow
380-
381-
# Policy Engine Lambda
382-
aws logs tail /aws/lambda/<PolicyEngineLambdaName> --follow
344+
The CDK stack outputs the function names for the two most commonly debugged Lambdas (`ProxyLambdaName`, `PreTokenGenerationLambdaName`). For the others, look up the auto-generated name in the AWS Console (Lambda → Functions, filter by stack name) or use the AWS CLI:
383345

384-
# Interceptor Lambda
385-
aws logs tail /aws/lambda/<InterceptorLambdaName> --follow
346+
```bash
347+
# MCP Proxy Lambda – name from CDK output: EnterpriseMcpInfraStack.ProxyLambdaName
348+
aws logs tail /aws/lambda/$(aws cloudformation describe-stacks \
349+
--stack-name EnterpriseMcpInfraStack \
350+
--query "Stacks[0].Outputs[?OutputKey=='ProxyLambdaName'].OutputValue" \
351+
--output text) --follow
352+
353+
# Pre-Token Generation Lambda – name from CDK output: EnterpriseMcpInfraStack.PreTokenGenerationLambdaName
354+
aws logs tail /aws/lambda/$(aws cloudformation describe-stacks \
355+
--stack-name EnterpriseMcpInfraStack \
356+
--query "Stacks[0].Outputs[?OutputKey=='PreTokenGenerationLambdaName'].OutputValue" \
357+
--output text) --follow
358+
359+
# Interceptor Lambda – look up name in AWS Console (filter by stack: EnterpriseMcpInfraStack)
360+
# aws logs tail /aws/lambda/<McpInterceptorLambda-name> --follow
361+
362+
# Policy Engine Lambda – look up name in AWS Console (filter by stack: EnterpriseMcpInfraStack)
363+
# aws logs tail /aws/lambda/<AgentCorePolicyEngine-PolicyFunction-name> --follow
386364
```
387365

388366
### CloudWatch Insights Queries
@@ -401,24 +379,66 @@ fields @timestamp, method, path, statusCode
401379
| sort @timestamp desc
402380
```
403381

404-
## Security Considerations
405-
406-
### Authentication
382+
## Security Posture
383+
384+
### Implemented
385+
386+
| Feature | Details |
387+
|---|---|
388+
| Cognito User Pool | Admin-only sign-up, strong password policy, Pre-Token Generation Lambda for audience/role claims |
389+
| OAuth 2.0 | Authorization Code Grant with custom scopes (`mcp.read`, `mcp.write`) |
390+
| JWT audience validation | Proxy Lambda validates `aud` claim before forwarding to AgentCore |
391+
| AgentCore Cognito authorizer | Token verified a second time by AWS at the gateway level |
392+
| Cedar policy engine | Fine-grained per-user tool access in ENFORCE mode |
393+
| Bedrock Guardrails | PII masking (address, name, email) and blocking (credit card numbers) via interceptor |
394+
| Lambda-in-VPC proxy | Private subnet, NAT egress only |
395+
| VPC Interface Endpoint | AgentCore traffic stays on AWS private network, never crosses public internet |
396+
| ALB TLS termination | TLS 1.2+ on custom domain via ACM certificate |
397+
| ALB `dropInvalidHeaderFields` | Rejects malformed headers (request-smuggling mitigation) |
398+
| ALB Host-header gating | Every forwarding rule requires Host header match; raw `*.elb` DNS returns 404 |
399+
| HTTP → HTTPS redirect | Permanent redirect on port 80 |
400+
| WAF WebACL | IP rate limit (1,000 req/5 min), AWS IP Reputation list, Core Rule Set (OWASP Top 10), Known Bad Inputs |
401+
| WAF Bot Control | COMMON level in COUNT mode (switch to BLOCK after traffic validation) |
402+
| Reserved Lambda concurrency | Caps on all functions to limit DoS blast radius |
403+
| Gateway resource policy | Restricts `InvokeGateway` to the VPC |
404+
| Shield Standard | Automatic L3/L4 DDoS protection on public ALBs |
405+
| ALB access logging | S3 bucket with SSE, public access blocked, SSL enforced, 90-day lifecycle expiration |
406+
| Redirect URI allowlist | `handle_callback` validates `redirect_uri` against registered Cognito callback URLs before issuing 302 redirects (prevents open-redirect / auth code theft) |
407+
| Per-Lambda IAM roles | Four dedicated least-privilege roles: `preTokenLambdaRole` (Cognito trigger), `proxyLambdaRole` (VPC + AgentCore invoke), `interceptorLambdaRole` (Bedrock Guardrails only), `toolLambdaRole` (CloudWatch Logs only) |
408+
409+
### Not Implemented – Consider Before Production
410+
411+
| Feature | Details |
412+
|---|---|
413+
| Shield Advanced | L7 DDoS protection, SRT access, cost protection (subscription required) |
414+
| Bot Control TARGETED | Higher inspection level for WAF Bot Control (additional cost) |
415+
| CloudTrail / Security Hub | Centralized audit and security findings |
416+
| ALB access-log Athena workgroup | Query access logs via Athena for forensic analysis |
417+
| GuardDuty findings | Threat detection integration |
418+
| MFA enforcement | Cognito User Pool is MFA-ready but not enforced (`mfa: cognito.Mfa.REQUIRED`) |
419+
| Scoped IAM resources | Several policies use `Resource: "*"` — scope to specific ARNs |
420+
| PKCE enforcement | Verify PKCE is enforced on the Cognito public client (no client secret) |
421+
| Log encryption | Lambda CloudWatch logs use default settings (no KMS CMK encryption) |
422+
| Log retention policy | Lambda CloudWatch log retention is indefinite by default |
423+
424+
### Additional Security Details
425+
426+
#### Authentication
407427
- OAuth 2.0 with PKCE (Proof Key for Code Exchange)
408428
- JWT tokens with custom claims
409429
- Secure token storage in VS Code
410430

411-
### Authorization
431+
#### Authorization
412432
- Policy-based access control using Cedar
413433
- User attribute injection via Lambda triggers
414434
- Gateway-level authorization enforcement
415435

416-
### Data Protection
436+
#### Data Protection
417437
- SSL/TLS encryption in transit
418438
- PII anonymization via Bedrock Guardrails
419-
- VPC isolation for ALB deployments
439+
- VPC isolation with private subnets and VPC endpoints
420440

421-
### Secrets Management
441+
#### Secrets Management
422442
- Client secrets stored in environment variables
423443
- OAuth tokens never exposed to logs
424444
- IAM role-based access for Lambda functions
@@ -452,10 +472,12 @@ cdk destroy
452472

453473
## Architecture Decisions
454474

455-
### Why Two Deployment Options?
475+
### Why ALB?
456476

457-
- **ALB**: Production environments requiring custom domains, SSL/TLS, and fine-grained routing
458-
- **API Gateway**: Development/testing, serverless preference, cost optimization
477+
- Production-grade with custom domains, SSL/TLS, and fine-grained routing
478+
- WAF WebACL integration for OWASP Top 10, rate limiting, and bot control
479+
- Access logging to S3 for forensic analysis
480+
- VPC integration for network isolation
459481

460482
### Why Lambda for MCP Servers?
461483

-30.6 KB
Loading

0 commit comments

Comments
 (0)