From 2e6d47fb72661d9d397d1d0c85615bc01f3d1a87 Mon Sep 17 00:00:00 2001 From: odinwang <48194321+odinwang@users.noreply.github.com> Date: Thu, 1 Oct 2026 18:11:15 +0800 Subject: [PATCH] Optional AgentCore Gateway interface endpoint in the platform VPC Runtimes in VPC mode reached every AgentCore Gateway (MCP tool gateways, and the agentcore_gateway model backend) through the NAT. With enable_gateway_vpce the platform VPC gets a com.amazonaws..bedrock-agentcore.gateway interface endpoint with private DNS, so *.gateway.bedrock-agentcore. resolves to endpoint ENIs and those calls stay inside the VPC with no URL change. The endpoint policy allows only InvokeGateway on this account's gateways. Principal stays "*": gateways with a CUSTOM_JWT authorizer cannot be matched on an IAM principal in an endpoint policy. The security group admits 443 from the VPC CIDR only. Off by default. Co-Authored-By: Claude Opus 5.5 (1M context) --- docs/architecture.md | 7 +++++ terraform/main.tf | 50 ++++++++++++++++++++++++++++++ terraform/terraform.tfvars.example | 4 +++ terraform/variables.tf | 6 ++++ 4 files changed, 67 insertions(+) diff --git a/docs/architecture.md b/docs/architecture.md index 1742b86..9d891e2 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -304,6 +304,13 @@ AgentCore Runtime ENIs → private subnets → NAT Gateway (Elastic IP) → inte allow-list. - `NetworkConfiguration` is not create-only: an existing runtime can be switched between PUBLIC and VPC without changing its ARN. +- Optional `enable_gateway_vpce` adds an AgentCore Gateway interface endpoint + (`com.amazonaws..bedrock-agentcore.gateway`, private DNS on). Calls + from runtimes to any gateway in the account (MCP tool gateways, and the + `agentcore_gateway` model backend) then stay inside the VPC instead of + leaving through the NAT, with no URL change. The endpoint policy allows + only `InvokeGateway` on this account's gateways; `Principal` stays `*` + because JWT-authorized gateways cannot be matched on an IAM principal. ### 4. Backend control plane (`backend/`) diff --git a/terraform/main.tf b/terraform/main.tf index 4022ce2..db13366 100644 --- a/terraform/main.tf +++ b/terraform/main.tf @@ -237,6 +237,56 @@ resource "aws_vpc_endpoint" "service_entry" { } } +# AgentCore Gateway interface endpoint. With private DNS on, every +# *.gateway.bedrock-agentcore. name resolved inside this VPC lands on +# the endpoint ENIs, so runtimes reach their gateways (MCP tools today, the +# agentcore_gateway model backend once enabled) without leaving the VPC and +# without any URL change. The policy narrows the endpoint to InvokeGateway +# on this account's gateways; Principal stays "*" because the team gateway +# authorizes with CUSTOM_JWT, which endpoint policies cannot match on. +data "aws_caller_identity" "current" {} + +resource "aws_security_group" "gateway_vpce" { + count = var.enable_gateway_vpce ? 1 : 0 + + name = "agent-platform-gateway-vpce${var.name_suffix}" + description = "AgentCore Gateway interface endpoint - HTTPS from inside the VPC" + vpc_id = module.network.vpc_id + + ingress { + description = "HTTPS from the platform VPC" + from_port = 443 + to_port = 443 + protocol = "tcp" + cidr_blocks = [module.network.vpc_cidr_block] + } +} + +resource "aws_vpc_endpoint" "agentcore_gateway" { + count = var.enable_gateway_vpce ? 1 : 0 + + vpc_id = module.network.vpc_id + service_name = "com.amazonaws.${var.aws_region}.bedrock-agentcore.gateway" + vpc_endpoint_type = "Interface" + subnet_ids = module.network.private_subnet_ids + security_group_ids = [aws_security_group.gateway_vpce[0].id] + private_dns_enabled = true + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [{ + Effect = "Allow" + Principal = "*" + Action = "bedrock-agentcore:InvokeGateway" + Resource = "arn:aws:bedrock-agentcore:${var.aws_region}:${data.aws_caller_identity.current.account_id}:gateway/*" + }] + }) + + tags = { + Name = "agent-platform-agentcore-gateway${var.name_suffix}" + } +} + module "mcp_hub_demo" { source = "./modules/mcp_hub_demo" count = local.mcp_hub_demo_on ? 1 : 0 diff --git a/terraform/terraform.tfvars.example b/terraform/terraform.tfvars.example index 620af26..9bb906d 100644 --- a/terraform/terraform.tfvars.example +++ b/terraform/terraform.tfvars.example @@ -49,6 +49,10 @@ oidc_issuer = "" # Caller interface-VPC-endpoint IDs for the private service-entry API. service_api_allowed_vpces = [] # e.g. ["vpce-0123456789abcdef0"] +# AgentCore Gateway interface endpoint (private DNS) in the platform VPC, so +# runtime -> gateway calls stay off the NAT path. ~3 AZ x endpoint-hour. +# enable_gateway_vpce = true + # --- EKS -------------------------------------------------------------------- # The containers run on a dedicated EKS cluster in the platform VPC (Graviton # managed node group, IRSA). Narrow the API endpoint to your operators' egress diff --git a/terraform/variables.tf b/terraform/variables.tf index 191ffb5..990d038 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -253,6 +253,12 @@ variable "entry_desired_count" { } } +variable "enable_gateway_vpce" { + description = "Interface VPC endpoint (private DNS) for AgentCore Gateway in the platform VPC, so runtime -> gateway traffic stays off the NAT path." + type = bool + default = false +} + variable "enable_mcp_hub_demo" { description = "Optional self-hosted MCP hub demo (requires enable_team_auth): a hub EC2 replacing AgentCore Gateway as the tool backend (MCPHUB-HMAC-SHA256 inbound), plus an EC2 playing the calling application. Package the hub source first — scripts/package_mcp_hub.sh." type = bool