AWS PrivateLink for Bedrock: Keep Coding-Agent Model Calls Off the Public Internet

0 views

Your coding-agent VPC locks sandboxes down with “no internet” security groups — then the planner Lambda reaches Bedrock through a NAT gateway because bedrock-runtime was never given a VPC endpoint. Model prompts, tool schemas, and retrieved monorepo chunks ride the public path. PrivateLink interface endpoints for Bedrock are the unfair advantage: Converse / InvokeModel / Agents runtime stay on AWS’s private network, NAT bills drop for that traffic, and endpoint policies bound which principals can talk to which model APIs. Pair with secret hygiene + VPC endpoint patterns and Organizations SCPs — this post is the private model-call path.

⚡ TL;DR: Create interface VPC endpoints for bedrock-runtime (and bedrock / bedrock-agent-runtime as needed) in the agent VPC. Point SDK calls at private DNS; disable public Bedrock access via SCP where required. Use endpoint policies to allow only approved model IDs and actions. Related: Prompt Management, Knowledge Bases RAG, Budgets.

Why coding agents care more than a simple chatbot

Agent traffic is fatter and more sensitive:

  • System prompts + tool schemas + retrieved source chunks
  • Multi-hop Converse with tool results that may include code
  • Cross-account shared agent platforms

Public egress forces you to open NAT for the planner ENI, which becomes the loophole sandboxes abuse (curl to random hosts). PrivateLink lets you deny internet on data-plane SGs while still calling Bedrock.

Path Model traffic Sandbox “no internet” Control
Public + NAT Via IGW/NAT Hard (planner needs NAT) Coarse SG/NACL
Public + NAT + egress proxy Via proxy Possible Ops heavy
PrivateLink bedrock-runtime Private ENIs ✅ Keep deny-all egress Endpoint policy + IAM
VPC Lattice / custom Varies Custom Usually overkill

Endpoints to create

Typical coding-agent control plane needs:

  1. com.amazonaws.region.bedrock-runtime — Converse, InvokeModel, streaming
  2. com.amazonaws.region.bedrock — control plane (list foundation models, etc.) if called from VPC
  3. com.amazonaws.region.bedrock-agent-runtime — Agents / Retrieve if used in-VPC
  4. com.amazonaws.region.bedrock-agent — Agents control plane if applicable

Also keep existing endpoints for Secrets Manager, S3 (gateway), ECR, Logs, STS — otherwise “private Bedrock” still fails on dependency calls. See Day‑36 for the broader hygiene bundle.

typescript
// ✅ Interface endpoint for bedrock-runtime (CDK)
import * as ec2 from "aws-cdk-lib/aws-ec2";

const bedrockRuntimeEp = new ec2.InterfaceVpcEndpoint(this, "BedrockRuntimeEp", {
  vpc,
  service: ec2.InterfaceVpcEndpointAwsService.BEDROCK_RUNTIME,
  subnets: { subnets: privateSubnets },
  securityGroups: [vpceSg],
  privateDnsEnabled: true, // ✅ SDKs keep default endpoint URL
  // Policy below — least privilege
});

bedrockRuntimeEp.addToPolicy(
  new iam.PolicyStatement({
    principals: [new iam.AnyPrincipal()],
    actions: [
      "bedrock:InvokeModel",
      "bedrock:InvokeModelWithResponseStream",
      "bedrock:Converse",
      "bedrock:ConverseStream",
    ],
    resources: [
      `arn:aws:bedrock:${region}::foundation-model/anthropic.*`,
      `arn:aws:bedrock:${region}:${account}:inference-profile/*`,
    ],
    conditions: {
      StringEquals: {
        "aws:PrincipalOrgID": orgId, // ✅ multi-account org lock
      },
    },
  })
);
python
# ✅ boto3 in VPC with private DNS — no custom endpoint_url needed
import boto3
client = boto3.client("bedrock-runtime", region_name="us-east-1")
# resolves to the PrivateLink ENI via private hosted zone
python
# ❌ Forcing public access from a locked-down subnet
client = boto3.client(
    "bedrock-runtime",
    endpoint_url="https://bedrock-runtime.us-east-1.amazonaws.com",
)
# without a route/NAT this hangs or fails — and with NAT you bypass PrivateLink intent

Security groups and DNS

  • VPCE SG: allow HTTPS 443 from planner / agent-runtime SGs only
  • Planner SG: egress 443 to VPCE SG (not 0.0.0.0/0)
  • Sandbox SG: still deny internet; sandboxes should not call Bedrock directly in most designs
  • Private DNS: enable so default SDK hostnames resolve to the endpoint

Verify with a canary Lambda in the VPC: Converse success + curl ifconfig.me failure.

Endpoint policies vs IAM vs SCP

Defense in depth:

  1. Identity IAM — task/role may bedrock:Converse on specific models
  2. Endpoint policy — even stolen keys outside the VPC cannot use this door without network path; inside VPC, policy limits actions/resources
  3. SCP — optionally Deny Bedrock APIs unless aws:SourceVpce matches approved endpoint IDs (careful with break-glass and non-VPC admin tooling)
json
// ✅ SCP fragment — Bedrock runtime only via approved VPCE (agent OU)
{
  "Effect": "Deny",
  "Action": [
    "bedrock:Converse",
    "bedrock:InvokeModel",
    "bedrock:InvokeModelWithResponseStream",
    "bedrock:ConverseStream"
  ],
  "Resource": "*",
  "Condition": {
    "StringNotEquals": {
      "aws:SourceVpce": ["vpce-0abc", "vpce-0def"]
    },
    "ArnLike": {
      "aws:PrincipalArn": ["arn:aws:iam::*:role/agent-*"]
    }
  }
}

Test SCPs in a non-prod OU first — they are sharp.

Multi-account patterns

Common layouts:

  • Shared services VPC in a network account hosts endpoints; spoke agent accounts use PrivateLink / RAM patterns carefully (many teams simply deploy endpoints per agent account VPC for blast-radius clarity)
  • Org-wide endpoint policy with aws:PrincipalOrgID
  • Centralize model allow-lists (inference profiles) in a governed account; spokes only Converse via profiles

Cross-account KB / Agents still need IAM resource policies — PrivateLink does not replace them; it only privatizes the network path (KB monorepo RAG).

Cost and ops notes

  • Interface endpoints bill per AZ ENI hours + data processing — usually cheaper than NAT for heavy Bedrock token egress, but not free; put endpoints in the AZs you actually use
  • Watch VPCE data processing vs former NAT GB
  • Cap model spend with Budgets + Cost Anomaly regardless of path
  • Streaming Converse works over PrivateLink; validate idle timeouts on NLB/ENI paths under long tool turns

Production checklist

  • [ ] bedrock-runtime (+ agent-runtime if needed) interface endpoints with private DNS
  • [ ] Dependency endpoints: SM, STS, Logs, ECR, S3
  • [ ] SG: 443 from planner only; sandboxes remain egress-deny
  • [ ] Endpoint policy allow-lists actions + model/inference-profile ARNs
  • [ ] Optional SCP: agent roles must use aws:SourceVpce
  • [ ] Canary: Converse OK, public egress fail
  • [ ] CloudTrail shows Bedrock calls; Lake queries for anomalies
  • [ ] Document break-glass for non-VPC admin Bedrock access

Streaming and long tool turns

ConverseStream over PrivateLink behaves like the public API, but long agent turns (retrieve → sandbox → Converse again) can sit idle on the TCP session while tools run. Keep tool execution out of band: end the model stream, run Fargate/Lambda tools, then start a new Converse with prior messages — do not hold a single streamed connection open across multi-minute Spot sandboxes. That pattern also plays nicer with VPCE idle timeouts and retries.

FAQ

Q: Does PrivateLink encrypt prompts better than public TLS?
A: TLS still applies; the win is network isolation and egress lock-down, not a new crypto algorithm. Combine with KMS/CMK where Bedrock features support customer keys.

Q: Gateway endpoint for Bedrock?
A: Bedrock uses interface endpoints, not S3-style gateway endpoints. Do not look for a gateway variant.

Q: Same pattern for OpenAI/Anthropic direct APIs?
A: Those need different designs (egress proxy, vendor private offers). This post is Bedrock on AWS PrivateLink — keep third-party calls out of the “no internet” sandbox path entirely.

PrivateLink for Bedrock lets you build coding agents that never open the internet just to think. Put bedrock-runtime on interface endpoints, lock policies to org and model ARNs, and keep sandboxes dark while planners still Converse at full speed.

Deep-dive PDF

Get the expanded guide for this post — extra diagrams-style checklists, failure modes, and a production walkthrough. Free when you subscribe to CheatCoders.

Already subscribed? or open the subscribe page.


Discover more from CheatCoders

Subscribe to get the latest posts sent to your email.

Comments

No comments yet. Why don’t you start the discussion?

Leave a comment

No account needed. Name and email are optional.