This workshop configures Solo Enterprise Agent Gateway to access AWS Bedrock without storing AWS credentials as Kubernetes secrets. Instead, we use EKS IAM Roles for Service Accounts (IRSA) to let the agentgateway pod assume an IAM role natively.
- No long-lived AWS access keys to manage or rotate
- Credentials are short-lived and automatically refreshed
- Fine-grained IAM permissions scoped to a single service account
- Follows AWS security best practices for EKS workloads
- An EKS cluster with Solo Enterprise Agent Gateway installed
kubectl,aws, andeksctlCLI tools installed and configured- AWS IAM permissions to create roles and OIDC providers
- Amazon Bedrock model access enabled in your region
Update these values for your environment.
export CLUSTER_NAME="<your-eks-cluster-name>"
export AWS_REGION="us-east-1"
export NAMESPACE="enterprise-agentgateway"
export SA_NAME="agentgateway"
export ROLE_NAME="agentgateway-bedrock-irsa"
# Auto-detect account ID
export ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
echo "Account ID: $ACCOUNT_ID"
echo "Cluster: $CLUSTER_NAME"
echo "Region: $AWS_REGION"kubectl config current-context
kubectl get ns $NAMESPACEaws bedrock get-foundation-model \
--model-identifier amazon.nova-micro-v1:0 \
--region $AWS_REGION \
--query 'modelDetails.{modelId:modelId,status:modelLifecycle.status}' \
--output tableIRSA works by:
- Associating the EKS cluster's OIDC issuer with AWS IAM
- Creating an IAM role that trusts a specific Kubernetes service account
- Annotating the service account with the role ARN
- EKS automatically injects temporary AWS credentials into the pod
This registers your cluster's OIDC issuer so IAM can validate service account tokens.
eksctl utils associate-iam-oidc-provider \
--cluster $CLUSTER_NAME \
--approveexport OIDC_ISSUER=$(aws eks describe-cluster \
--name $CLUSTER_NAME \
--query "cluster.identity.oidc.issuer" \
--output text)
export OIDC_ID=$(echo $OIDC_ISSUER | sed 's|.*/id/||')
echo "OIDC Issuer: $OIDC_ISSUER"
echo "OIDC ID: $OIDC_ID"This policy allows only the agentgateway service account in the enterprise-agentgateway namespace to assume the role.
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:${NAMESPACE}:${SA_NAME}",
"oidc.eks.${AWS_REGION}.amazonaws.com/id/${OIDC_ID}:aud": "sts.amazonaws.com"
}
}
}
]
}
EOF
cat /tmp/trust-policy.json | python3 -m json.toolaws iam create-role \
--role-name $ROLE_NAME \
--assume-role-policy-document file:///tmp/trust-policy.json \
--description "IRSA role for agentgateway to access Amazon Bedrock" \
--query 'Role.Arn' \
--output textaws iam attach-role-policy \
--role-name $ROLE_NAME \
--policy-arn arn:aws:iam::aws:policy/AmazonBedrockFullAccess
echo "Attached policies:"
aws iam list-attached-role-policies \
--role-name $ROLE_NAME \
--query 'AttachedPolicies[].PolicyName' \
--output tableNow we configure agentgateway to use IRSA for Bedrock access. The key difference from secret-based auth is:
- The
AgentgatewayBackendomitspolicies.auth(no secret reference) - The
EnterpriseAgentgatewayParametersannotates the data plane service account with the IAM role ARN
Note: there is no policies.auth section. This signals to agentgateway to use the pod's own AWS credentials (provided via IRSA).
cat <<EOF | kubectl apply -f -
apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayBackend
metadata:
name: bedrock
namespace: $NAMESPACE
spec:
ai:
provider:
bedrock:
model: "amazon.nova-micro-v1:0"
region: "$AWS_REGION"
EOF
# Verify the backend is accepted
kubectl get agentgatewaybackend bedrock -n $NAMESPACE \
-o jsonpath='{.status.conditions[0].message}'This patches the EnterpriseAgentgatewayParameters to add the eks.amazonaws.com/role-arn annotation to the agentgateway data plane service account. EKS will then inject AWS credentials into the pod.
export ROLE_ARN="arn:aws:iam::${ACCOUNT_ID}:role/${ROLE_NAME}"
kubectl patch enterpriseagentgatewayparameters agentgateway-params \
-n $NAMESPACE \
--type merge \
-p '{
"spec": {
"serviceAccount": {
"metadata": {
"annotations": {
"eks.amazonaws.com/role-arn": "'$ROLE_ARN'"
}
}
}
}
}'
# Verify annotation on service account
kubectl get sa $SA_NAME -n $NAMESPACE \
-o jsonpath='{.metadata.annotations}' | python3 -m json.toolRoute traffic through the agentgateway to the Bedrock backend.
cat <<EOF | kubectl apply -f -
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: bedrock
namespace: $NAMESPACE
spec:
parentRefs:
- name: agentgateway
namespace: $NAMESPACE
rules:
- backendRefs:
- name: bedrock
namespace: $NAMESPACE
group: agentgateway.dev
kind: AgentgatewayBackend
EOF
# Verify the route is accepted
kubectl get httproute bedrock -n $NAMESPACE \
-o jsonpath='{.status.parents[0].conditions[0].reason}'The pods need to be restarted so the EKS mutating webhook can inject the IRSA projected token and environment variables.
kubectl rollout restart deployment agentgateway -n $NAMESPACE
kubectl rollout status deployment agentgateway -n $NAMESPACE --timeout=120sConfirm that the EKS webhook injected the correct environment variables and projected token volume into the agentgateway pod.
POD=$(kubectl get pods -n $NAMESPACE \
-l app.kubernetes.io/name=agentgateway \
-o jsonpath='{.items[0].metadata.name}')
echo "Pod: $POD"
echo ""
echo "=== IRSA Environment Variables ==="
kubectl get pod $POD -n $NAMESPACE \
-o jsonpath='{range .spec.containers[0].env[*]}{.name}={.value}{"\n"}{end}' \
| grep -E "AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE|AWS_REGION"
echo ""
echo "=== Projected Token Volume ==="
kubectl get pod $POD -n $NAMESPACE \
-o jsonpath='{.spec.volumes[?(@.name=="aws-iam-token")]}' | python3 -m json.toolYou should see:
AWS_ROLE_ARNset to your IAM role ARNAWS_WEB_IDENTITY_TOKEN_FILEset to/var/run/secrets/eks.amazonaws.com/serviceaccount/token- A projected volume named
aws-iam-tokenwith audiencests.amazonaws.com
Send a chat completion request through the agentgateway to verify end-to-end connectivity with Bedrock using IRSA credentials.
export GATEWAY_IP=$(kubectl get svc agentgateway -n $NAMESPACE \
-o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
export GATEWAY_PORT=8080
# If no external LB, fall back to port-forward
if [ -z "$GATEWAY_IP" ]; then
echo "No external LB found. Using port-forward..."
kubectl port-forward -n $NAMESPACE svc/agentgateway 18080:8080 &
sleep 2
export GATEWAY_IP="localhost"
export GATEWAY_PORT=18080
fi
echo "Gateway: http://${GATEWAY_IP}:${GATEWAY_PORT}"curl -s -X POST "http://${GATEWAY_IP}:${GATEWAY_PORT}/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"model": "bedrock/amazon.nova-micro-v1:0",
"messages": [
{"role": "user", "content": "Say hello in one sentence."}
]
}' | python3 -m json.toolYou should see a successful JSON response with:
model:amazon.nova-micro-v1:0choices[0].message.content: A greeting from the modelusage: Token counts for the request
curl -s -X POST "http://${GATEWAY_IP}:${GATEWAY_PORT}/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"model": "bedrock/amazon.nova-micro-v1:0",
"messages": [
{"role": "system", "content": "You are a helpful assistant. Be concise."},
{"role": "user", "content": "What are 3 benefits of using IRSA over static AWS credentials?"}
]
}' | python3 -m json.toolTo remove the resources created in this workshop:
# Remove Kubernetes resources
kubectl delete httproute bedrock -n $NAMESPACE
kubectl delete agentgatewaybackend bedrock -n $NAMESPACE
# Remove IRSA annotation from service account
kubectl patch enterpriseagentgatewayparameters agentgateway-params \
-n $NAMESPACE \
--type json \
-p '[{"op": "remove", "path": "/spec/serviceAccount"}]'
# Restart pods to drop IRSA credentials
kubectl rollout restart deployment agentgateway -n $NAMESPACE
# Remove AWS resources
aws iam detach-role-policy \
--role-name $ROLE_NAME \
--policy-arn arn:aws:iam::aws:policy/AmazonBedrockFullAccess
aws iam delete-role --role-name $ROLE_NAME
echo "Cleanup complete."In this workshop you:
- Associated the EKS OIDC provider with AWS IAM to enable workload identity federation
- Created an IAM role with a trust policy scoped to the agentgateway service account and attached Bedrock permissions
- Deployed an AgentgatewayBackend for Bedrock without any secret references, relying on IRSA for authentication
- Annotated the service account via
EnterpriseAgentgatewayParametersso EKS injects temporary credentials - Verified that the pod received IRSA credentials and successfully called Bedrock
| Approach | Credentials | Rotation | Scope |
|---|---|---|---|
| Secret-based | Static access key in K8s Secret | Manual | Any pod with secret access |
| IRSA | Temporary STS credentials | Automatic | Single service account |
IRSA is the recommended approach for production EKS deployments accessing AWS services.