Skip to content

Instantly share code, notes, and snippets.

@rvennam
Created April 13, 2026 16:03
Show Gist options
  • Select an option

  • Save rvennam/e6e8d1e2f44f5a6f303629bcef6ec599 to your computer and use it in GitHub Desktop.

Select an option

Save rvennam/e6e8d1e2f44f5a6f303629bcef6ec599 to your computer and use it in GitHub Desktop.
Workshop: Native AWS Bedrock AgentCore Routing with Solo Enterprise Agent Gateway v2.3.0 (no proxy needed)

Workshop: Native AWS Bedrock AgentCore Routing with Solo Enterprise Agent Gateway v2.3.0

Overview

Solo Enterprise Agent Gateway v2.3.0 introduces native AWS Bedrock AgentCore backend support — no sidecar proxy, no custom container, no extra hop. The gateway signs requests with SigV4 using the correct bedrock-agentcore service name and routes A2A traffic directly to your AgentCore runtime agents.

In this workshop you will:

  1. Install Enterprise Agent Gateway v2.3.0 on EKS
  2. Configure IRSA so the gateway can call AgentCore natively
  3. Create an AgentgatewayBackend with the new spec.aws.agentCore type
  4. Route A2A traffic directly to AgentCore — zero proxies
  5. Apply enterprise policies (JWT auth, rate limiting) to the route

Architecture

Before v2.3.0 — proxy required

graph LR
    Client([Client]) -->|A2A| GW[Agent Gateway]
    GW -->|A2A| Proxy[A2A Proxy Pod<br/>Flask + boto3]
    Proxy -->|SigV4 bedrock-agentcore| AC[AWS Bedrock<br/>AgentCore]
    
    style Proxy fill:#f66,stroke:#333,color:#fff
    style GW fill:#4a9eff,stroke:#333,color:#fff
    style AC fill:#ff9900,stroke:#333,color:#fff
Loading

The proxy was necessary because the gateway hardcoded SigV4 signing to the bedrock service name. AgentCore requires bedrock-agentcore.

v2.3.0 — native routing, no proxy

graph LR
    Client([Client]) -->|A2A| GW[Agent Gateway]
    GW -->|SigV4 bedrock-agentcore| AC[AWS Bedrock<br/>AgentCore]
    
    style GW fill:#4a9eff,stroke:#333,color:#fff
    style AC fill:#ff9900,stroke:#333,color:#fff
Loading

The gateway now handles SigV4 signing with the correct service name natively.


Detailed Request Flow

sequenceDiagram
    participant C as Client
    participant GW as Agent Gateway<br/>(EKS Pod with IRSA)
    participant STS as AWS STS
    participant AC as Bedrock AgentCore

    C->>GW: POST /tasks/send (A2A JSON-RPC)
    
    Note over GW: Matches HTTPRoute → AgentgatewayBackend<br/>type: aws.agentCore

    GW->>STS: AssumeRoleWithWebIdentity<br/>(IRSA token → temporary credentials)
    STS-->>GW: Temporary AWS credentials

    Note over GW: Signs request with SigV4<br/>service = "bedrock-agentcore"

    GW->>AC: InvokeAgentRuntime<br/>(SigV4-signed HTTPS)
    AC-->>GW: Agent response (streaming)
    GW-->>C: A2A response (JSON-RPC result)
Loading

What Changed in the CRD

v2.3.0 adds a new spec.aws backend type alongside the existing spec.ai, spec.mcp, and spec.static types:

graph TD
    Backend[AgentgatewayBackend] --> AI[spec.ai<br/>LLM Providers]
    Backend --> MCP[spec.mcp<br/>MCP Servers]
    Backend --> Static[spec.static<br/>Static Host]
    Backend --> AWS[spec.aws<br/>AWS Services]
    
    AI --> OpenAI[OpenAI]
    AI --> Anthropic[Anthropic]
    AI --> Bedrock[Bedrock]
    AI --> Azure[Azure OpenAI]
    AI --> Gemini[Gemini]
    
    AWS --> AgentCore[agentCore<br/>agentRuntimeArn<br/>qualifier]
    
    style AWS fill:#ff9900,stroke:#333,color:#fff
    style AgentCore fill:#ff9900,stroke:#333,color:#fff
Loading

Prerequisites

  • An EKS cluster (v1.28+)
  • kubectl, aws, helm, and eksctl CLI tools installed
  • AWS IAM permissions to create roles, policies, and OIDC providers
  • A Bedrock AgentCore runtime agent deployed (you need the ARN)
  • A Solo Enterprise Agent Gateway license key

Step 1: Set Variables

export CLUSTER_NAME="<your-eks-cluster-name>"
export AWS_REGION="us-east-1"
export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)

# Your AgentCore runtime ARN
export AGENT_RUNTIME_ARN="arn:aws:bedrock-agentcore:${AWS_REGION}:${ACCOUNT_ID}:runtime/<your-runtime-id>"

# Enterprise Agent Gateway license
export AGENTGATEWAY_LICENSE_KEY="<your-license-key>"

echo "Cluster:  ${CLUSTER_NAME}"
echo "Account:  ${ACCOUNT_ID}"
echo "Region:   ${AWS_REGION}"
echo "Runtime:  ${AGENT_RUNTIME_ARN}"

Step 2: Install Enterprise Agent Gateway v2.3.0

2a: Install CRDs

helm upgrade -i enterprise-agentgateway-crds \
  oci://us-docker.pkg.dev/solo-public/enterprise-agentgateway/charts/enterprise-agentgateway-crds \
  --create-namespace --namespace agentgateway-system \
  --version v2.3.0

2b: Install the control plane

helm upgrade -i enterprise-agentgateway \
  oci://us-docker.pkg.dev/solo-public/enterprise-agentgateway/charts/enterprise-agentgateway \
  --namespace agentgateway-system \
  --set-string licensing.licenseKey=${AGENTGATEWAY_LICENSE_KEY} \
  --version v2.3.0

2c: Verify all pods are running

kubectl get pods -n agentgateway-system

Expected output:

NAME                                                       READY   STATUS    RESTARTS   AGE
enterprise-agentgateway-xxxxxxxxx-xxxxx                    1/1     Running   0          30s
ext-auth-service-enterprise-agentgateway-xxxxxxxxx-xxxxx   1/1     Running   0          30s
ext-cache-enterprise-agentgateway-xxxxxxxxx-xxxxx          1/1     Running   0          30s
rate-limiter-enterprise-agentgateway-xxxxxxxxx-xxxxx       1/1     Running   0          30s

Step 3: Create the Gateway

kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: agentgateway
  namespace: agentgateway-system
spec:
  gatewayClassName: enterprise-agentgateway
  listeners:
  - name: http
    port: 8080
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: Same
EOF

Wait for it to be programmed:

kubectl wait --for=condition=programmed gateway/agentgateway \
  -n agentgateway-system --timeout=120s

Step 4: Configure IRSA for the Gateway Proxy

The Gateway controller creates a proxy pod with its own service account (agentgateway). This proxy pod is the one that actually calls AgentCore, so it needs AWS credentials via IRSA.

graph TD
    subgraph EKS Cluster
        subgraph agentgateway-system namespace
            Controller[Controller Pod<br/>SA: enterprise-agentgateway]
            Proxy[Proxy Pod<br/>SA: agentgateway<br/>+ IRSA annotation]
        end
    end
    
    subgraph AWS IAM
        Role[IAM Role<br/>agentgateway-irsa]
        Policy[Policy<br/>bedrock-agentcore:*]
    end
    
    Proxy -.->|AssumeRoleWithWebIdentity| Role
    Role --> Policy
    
    style Proxy fill:#4a9eff,stroke:#333,color:#fff
    style Role fill:#ff9900,stroke:#333,color:#fff
    style Policy fill:#ff9900,stroke:#333,color:#fff
Loading

4a: Associate the EKS OIDC provider with IAM

eksctl utils associate-iam-oidc-provider \
  --cluster $CLUSTER_NAME \
  --approve

4b: Get the OIDC provider ID

export OIDC_ID=$(aws eks describe-cluster \
  --name $CLUSTER_NAME \
  --query "cluster.identity.oidc.issuer" \
  --output text | sed 's|.*/id/||')

echo "OIDC ID: $OIDC_ID"

4c: Create the IAM policy

cat > /tmp/agentcore-policy.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "bedrock-agentcore:InvokeAgentRuntime",
      "Resource": [
        "${AGENT_RUNTIME_ARN}",
        "${AGENT_RUNTIME_ARN}/*"
      ]
    }
  ]
}
EOF

aws iam create-policy \
  --policy-name AgentGatewayAgentCoreAccess \
  --policy-document file:///tmp/agentcore-policy.json

4d: Create the IAM role with trust policy for the proxy SA

cat > /tmp/trust-policy.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::${ACCOUNT_ID}:oidc-provider/oidc.eks.${AWS_REGION}.amazonaws.com/id/${OIDC_ID}"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "oidc.eks.${AWS_REGION}.amazonaws.com/id/${OIDC_ID}:sub": "system:serviceaccount:agentgateway-system:agentgateway",
          "oidc.eks.${AWS_REGION}.amazonaws.com/id/${OIDC_ID}:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}
EOF

aws iam create-role \
  --role-name agentgateway-agentcore-irsa \
  --assume-role-policy-document file:///tmp/trust-policy.json

aws iam attach-role-policy \
  --role-name agentgateway-agentcore-irsa \
  --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/AgentGatewayAgentCoreAccess

export ROLE_ARN="arn:aws:iam::${ACCOUNT_ID}:role/agentgateway-agentcore-irsa"

4e: Annotate the proxy service account and restart the pod

The Gateway controller creates the agentgateway service account automatically. Annotate it with the IRSA role:

kubectl annotate sa agentgateway -n agentgateway-system \
  eks.amazonaws.com/role-arn=${ROLE_ARN} --overwrite

# Restart the proxy pod to pick up the IRSA token
kubectl delete pod -n agentgateway-system \
  -l gateway.networking.k8s.io/gateway-name=agentgateway

4f: Verify IRSA injection

PROXY_POD=$(kubectl get pods -n agentgateway-system \
  -l gateway.networking.k8s.io/gateway-name=agentgateway \
  -o jsonpath='{.items[0].metadata.name}')

kubectl get pod $PROXY_POD -n agentgateway-system \
  -o jsonpath='{range .spec.containers[*].env[*]}{.name}={.value}{"\n"}{end}' \
  | grep -E "AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE|AWS_REGION"

Expected output:

AWS_STS_REGIONAL_ENDPOINTS=regional
AWS_DEFAULT_REGION=us-east-1
AWS_REGION=us-east-1
AWS_ROLE_ARN=arn:aws:iam::<account>:role/agentgateway-agentcore-irsa
AWS_WEB_IDENTITY_TOKEN_FILE=/var/run/secrets/eks.amazonaws.com/serviceaccount/token

Step 5: Create the AgentCore Backend

This is the key new feature in v2.3.0 — spec.aws.agentCore:

kubectl apply -f - <<EOF
apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayBackend
metadata:
  name: agentcore-backend
  namespace: agentgateway-system
spec:
  aws:
    agentCore:
      agentRuntimeArn: "${AGENT_RUNTIME_ARN}"
EOF

Verify it's accepted:

kubectl get agentgatewaybackend agentcore-backend -n agentgateway-system \
  -o jsonpath='{.status.conditions[0].reason}'
# Expected: Accepted

Step 6: Create the HTTPRoute

Route all A2A traffic to the AgentCore backend:

kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: agentcore
  namespace: agentgateway-system
spec:
  parentRefs:
  - name: agentgateway
    namespace: agentgateway-system
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - group: agentgateway.dev
      kind: AgentgatewayBackend
      name: agentcore-backend
      namespace: agentgateway-system
EOF

Verify the route is resolved:

kubectl get httproute agentcore -n agentgateway-system \
  -o jsonpath='{.status.parents[0].conditions[?(@.type=="ResolvedRefs")].reason}'
# Expected: ResolvedRefs

Step 7: Test End-to-End

Option A: Port-forward (for testing)

kubectl port-forward -n agentgateway-system svc/agentgateway 8080:8080 &
export GW_URL="http://localhost:8080"

Option B: Use the LoadBalancer address

export GW_URL="http://$(kubectl get svc agentgateway -n agentgateway-system \
  -o jsonpath='{.status.loadBalancer.ingress[0].hostname}'):8080"

Send an A2A request

curl -s -X POST ${GW_URL}/tasks/send \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc": "2.0",
    "id": "test-1",
    "method": "tasks/send",
    "params": {
      "id": "task-1",
      "message": {
        "role": "user",
        "parts": [
          {
            "type": "text",
            "text": "Hello from Agent Gateway! What can you help me with?"
          }
        ]
      }
    }
  }' | jq

Expected response:

{
  "result": {
    "role": "assistant",
    "content": [
      {
        "text": "Hello! I'm here to help you with things like answering questions, writing, analysis, brainstorming, problem-solving, and more. What's on your mind?"
      }
    ]
  }
}

The request flows directly: curl -> Agent Gateway -> AgentCore (SigV4 signed with bedrock-agentcore). No proxy pod, no extra hop.


Step 8: Apply Enterprise Policies

With traffic flowing through the gateway, you can layer enterprise policies on the route.

8a: Rate Limiting

# Create RateLimitConfig
kubectl apply -f - <<EOF
apiVersion: ratelimit.solo.io/v1alpha1
kind: RateLimitConfig
metadata:
  name: agentcore-rate-limit
  namespace: agentgateway-system
spec:
  raw:
    descriptors:
    - key: generic_key
      value: counter
      rateLimit:
        requestsPerUnit: 5
        unit: MINUTE
    rateLimits:
    - actions:
      - genericKey:
          descriptorValue: counter
      type: REQUEST
---
# Apply rate limit policy to the route
apiVersion: enterpriseagentgateway.solo.io/v1alpha1
kind: EnterpriseAgentgatewayPolicy
metadata:
  name: agentcore-rate-limit
  namespace: agentgateway-system
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: agentcore
  traffic:
    entRateLimit:
      global:
        rateLimitConfigRefs:
        - name: agentcore-rate-limit
          namespace: agentgateway-system
EOF

Test rate limiting:

for i in $(seq 1 8); do
  CODE=$(curl -s -o /dev/null -w "%{http_code}" -X POST ${GW_URL}/tasks/send \
    -H "Content-Type: application/json" \
    -d '{"jsonrpc":"2.0","id":"'$i'","method":"tasks/send","params":{"id":"t'$i'","message":{"role":"user","parts":[{"type":"text","text":"ping"}]}}}')
  echo "Request $i: HTTP $CODE"
done

After 5 requests, you should see HTTP 429.

8b: JWT Authentication

kubectl apply -f - <<EOF
apiVersion: enterpriseagentgateway.solo.io/v1alpha1
kind: EnterpriseAgentgatewayPolicy
metadata:
  name: agentcore-jwt-auth
  namespace: agentgateway-system
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: agentcore
  traffic:
    jwtAuthentication:
      mode: Strict
      providers:
      - issuer: "https://your-idp.example.com"
        audiences: ["agentcore"]
        jwks:
          remote:
            url: "https://your-idp.example.com/.well-known/jwks.json"
EOF

Test without token (expect 401):

curl -s -o /dev/null -w "%{http_code}" -X POST ${GW_URL}/tasks/send \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":"1","method":"tasks/send","params":{"id":"t1","message":{"role":"user","parts":[{"type":"text","text":"test"}]}}}'
# Expected: 401

Before vs. After: What You No Longer Need

graph TB
    subgraph "Before v2.3.0"
        direction TB
        B1[Agent Gateway] --> B2[A2A Proxy Deployment<br/>Flask + gunicorn + boto3]
        B2 --> B3[A2A Proxy Service<br/>appProtocol: kgateway.dev/a2a]
        B2 --> B4[Proxy IRSA SA<br/>+ ECR image + Dockerfile]
        B2 --> B5[AgentCore]
        
        style B2 fill:#f66,stroke:#333,color:#fff
        style B3 fill:#f66,stroke:#333,color:#fff
        style B4 fill:#f66,stroke:#333,color:#fff
    end
    
    subgraph "v2.3.0"
        direction TB
        A1[Agent Gateway] --> A2[AgentCore]
        A1 -.- A3[AgentgatewayBackend<br/>spec.aws.agentCore]
        
        style A1 fill:#4a9eff,stroke:#333,color:#fff
        style A3 fill:#4a9eff,stroke:#333,color:#fff
    end
Loading
Component Before v2.3.0 v2.3.0+
Proxy pod Required (Flask + boto3 + gunicorn) Eliminated
Container image Build, push to ECR, maintain Not needed
Extra Service + Deployment Required in separate namespace Not needed
IRSA setup On proxy service account On gateway proxy SA
SigV4 signing boto3 in proxy code Native in gateway
Backend CRD HTTPRoute -> Service (A2A) AgentgatewayBackend with spec.aws.agentCore
Latency Extra network hop through proxy Direct to AgentCore
Failure modes Proxy pod crashes, OOM, scaling One less thing to break

Cleanup

# Remove Kubernetes resources
kubectl delete httproute agentcore -n agentgateway-system
kubectl delete agentgatewaybackend agentcore-backend -n agentgateway-system
kubectl delete gateway agentgateway -n agentgateway-system

# Uninstall Helm releases
helm uninstall enterprise-agentgateway -n agentgateway-system
helm uninstall enterprise-agentgateway-crds -n agentgateway-system
kubectl delete ns agentgateway-system

# Remove IAM resources
aws iam detach-role-policy \
  --role-name agentgateway-agentcore-irsa \
  --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/AgentGatewayAgentCoreAccess
aws iam delete-role --role-name agentgateway-agentcore-irsa
aws iam delete-policy \
  --policy-arn arn:aws:iam::${ACCOUNT_ID}:policy/AgentGatewayAgentCoreAccess

Summary

What How
Gateway version Enterprise Agent Gateway v2.3.0
Backend type spec.aws.agentCore with agentRuntimeArn
AWS credentials IRSA on the gateway proxy service account
SigV4 signing Native — service name bedrock-agentcore handled automatically
Protocol A2A (JSON-RPC tasks/send) in, SigV4 HTTPS out
Enterprise policies JWT auth, rate limiting, CORS via EnterpriseAgentgatewayPolicy
Proxy pod Not needed

References

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment