For the complete documentation index, see llms.txt. Markdown versions of all docs pages are available by appending .md to any docs URL.
Ambient Egress
Configure agentgateway as the egress gateway for an ambient mesh
Route AI traffic from an Istio ambient mesh through agentgateway to external providers.
About ambient mesh
Solo.io and Google collaborated to develop ambient mesh, a sidecarless architecture for the Istio service mesh. Ambient mesh uses node-level ztunnels to route and secure Layer 4 traffic with mTLS. For Layer 7 policy and routing, ztunnel forwards traffic to waypoint proxies over HBONE.
To learn more, see the Istio ambient overview and the waypoint configuration guide.
About this guide
In this guide, you configure an agentgateway-managed waypoint for ambient egress. A client workload in an ambient mesh sends requests directly to an external host. Istio detects the destination through a ServiceEntry and routes the request through the waypoint, where agentgateway applies routing rules.
In this guide, external means the destination is modeled as MESH_EXTERNAL in the ServiceEntry and is
outside your in-mesh service namespace and waypoint policy boundary.
To keep this demo free and repeatable, the external destination is httpbin.org.
The route adds an x-agw-waypoint: true response header so you can verify that traffic passed through
agentgateway.
flowchart LR
client[Client workload<br/>Istio identity] -->|Request to external host| ztunnel[Istio ztunnel]
ztunnel -->|HBONE over ambient mTLS| gateway[agentgateway<br/>egress waypoint]
gateway -->|HTTP request| provider[httpbin.org<br/>External service]
The client does not use the waypoint address. It calls the backend host directly, and Istio transparently forwards matching egress traffic through the waypoint.
Before you begin
Follow the Get started guide to install agentgateway.
Follow the Sample app guide to create a gateway proxy with an HTTP listener and deploy the httpbin sample app.
Get the external address of the gateway and save it in an environment variable.
Tip
Kind cluster? Kind does not support
LoadBalancerservices by default. To use this option with a Kind cluster, install and runcloud-provider-kind.export INGRESS_GW_ADDRESS=$(kubectl get svc -n agentgateway-system agentgateway-proxy -o jsonpath="{.status.loadBalancer.ingress[0]['hostname','ip']}") echo $INGRESS_GW_ADDRESS
Step 1: Set up an ambient mesh
Set up an ambient mesh in your cluster to secure service-to-service communication with mutual TLS by following the ambientmesh.io quickstart documentation.Step 2: Run preflight checks
Verify your current context, required control planes, and available GatewayClasses.
kubectl config current-context
kubectl get gatewayclass -o custom-columns=NAME:.metadata.name,CONTROLLER:.spec.controllerName
kubectl -n istio-system get pods -l app=ztunnel
kubectl -n agentgateway-system get deploy,podsMake sure an agentgateway-owned GatewayClass exists (commonly agentgateway).
Step 3: Create namespaces and a test client
Create an ambient-enabled client namespace and a separate namespace for the egress waypoint.
Note
Ambient-enabled means Istio configures ztunnel capture for workloads in the client namespace so outbound traffic is transparently intercepted and secured with mTLS, without sidecars.
kubectl apply -f - <<EOF
apiVersion: v1
kind: Namespace
metadata:
name: agents
labels:
istio.io/dataplane-mode: ambient
---
apiVersion: v1
kind: Namespace
metadata:
name: agentgateway-egress
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: curl
namespace: agents
spec:
replicas: 1
selector:
matchLabels:
app: curl
template:
metadata:
labels:
app: curl
spec:
containers:
- name: curl
image: curlimages/curl:8.10.1
command: ["sleep", "infinity"]
EOF
kubectl -n agents rollout status deploy/curlBy default, leave the agentgateway-egress namespace unlabeled for ambient mode.
If you do label that namespace with istio.io/dataplane-mode=ambient, make sure the waypoint pods
opt out with istio.io/dataplane-mode=none; otherwise ztunnel capture can conflict with the
waypoint’s HBONE listener on port 15008.
Step 4: Deploy an egress waypoint
Create AgentgatewayParameters for Istio integration and force the generated Service to ClusterIP.
Then deploy a waypoint Gateway with:
- An HBONE listener on port
15008. - An internal HTTP listener on port
80for route attachment.
The internal listener is a routing attachment point, not a direct entry socket. Marking that
port as internal makes it routing-only (no generated Service port, container port, or direct
listener socket) while still giving you a clear, stable attachment point for HTTPRoute policy
and backend selection.
If you want namespace-level egress isolation for ambient clients, use the optional NetworkPolicy in
Step 6 to allow only DNS, Istio system, and waypoint-bound traffic.
kubectl apply -f - <<EOF
apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayParameters
metadata:
name: agw-waypoint-params
namespace: agentgateway-egress
spec:
istio:
enabled: true
service:
spec:
type: ClusterIP
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: agw-waypoint
namespace: agentgateway-egress
labels:
istio.io/waypoint-for: service
annotations:
agentgateway.dev/internal-ports: "80"
spec:
gatewayClassName: agentgateway
infrastructure:
parametersRef:
group: agentgateway.dev
kind: AgentgatewayParameters
name: agw-waypoint-params
listeners:
- name: mesh
port: 15008
protocol: HBONE
allowedRoutes:
namespaces:
from: All
- name: inner-http
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: All
EOF
kubectl -n agentgateway-egress wait --for=condition=Programmed gateway/agw-waypoint --timeout=2mStep 5: Bind the external destination and configure routing
Create:
- A ServiceEntry for the external host.
- An AgentgatewayBackend that targets the external host.
- An HTTPRoute attached to the waypoint
inner-httplistener that adds a response header.
These resources serve different roles and are both required.
The ServiceEntry tells Istio that httpbin.org is an external destination (MESH_EXTERNAL) and
enables ambient waypoint steering for that host. The AgentgatewayBackend tells agentgateway where to
forward traffic after the route matches. Without the ServiceEntry, Istio might bypass waypoint-based
egress handling for the host. Without the AgentgatewayBackend, the route has no upstream target.
For ServiceEntry fields and behavior, see the Istio ServiceEntry reference.
kubectl apply -f - <<EOF
apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
name: httpbin-external
namespace: agents
labels:
istio.io/use-waypoint: agw-waypoint
istio.io/use-waypoint-namespace: agentgateway-egress
spec:
hosts:
- httpbin.org
location: MESH_EXTERNAL
resolution: DNS
ports:
- number: 80
name: http
protocol: HTTP
---
apiVersion: agentgateway.dev/v1alpha1
kind: AgentgatewayBackend
metadata:
name: httpbin-backend
namespace: agentgateway-egress
spec:
static:
host: httpbin.org
port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: httpbin-via-agw
namespace: agentgateway-egress
spec:
parentRefs:
- name: agw-waypoint
sectionName: inner-http
hostnames:
- httpbin.org
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- group: agentgateway.dev
kind: AgentgatewayBackend
name: httpbin-backend
filters:
- type: ResponseHeaderModifier
responseHeaderModifier:
add:
- name: x-agw-waypoint
value: "true"
EOF
kubectl -n agentgateway-egress wait --for=jsonpath='{.status.parents[0].conditions[?(@.type=="Accepted")].status}'=True \
httproute/httpbin-via-agw --timeout=2mStep 6: Optional: restrict egress from ambient clients with NetworkPolicy
To follow the Istio egress hardening pattern, apply an egress policy in the agents namespace.
This policy allows egress only to:
- DNS in
kube-systemon port53. istio-systemcomponents.- The waypoint namespace on HBONE port
15008.
Network policies are enforced by your Kubernetes CNI plugin, so behavior can vary by cluster.
For ambient mode, this policy is applicable to source workloads in the client namespace; make sure
waypoint traffic (TCP 15008) and DNS are explicitly allowed, or egress requests can fail.
kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-egress-to-istio-system-and-kube-dns
namespace: agents
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: istio-system
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: agentgateway-egress
ports:
- protocol: TCP
port: 15008
EOFStep 7: Send a request directly to the backend host
From the ambient client, send a request to the external destination address.
kubectl -n agents exec deploy/curl -- curl -sSI http://httpbin.org/getVerify these response properties:
- HTTP status is
200. - Header
x-agw-waypoint: trueis present.
Example output:
HTTP/1.1 200 OK
access-control-allow-credentials: true
access-control-allow-origin: *
content-type: application/json; charset=utf-8
x-agw-waypoint: true
Step 8: Verify ambient transport and waypoint attachment
Confirm that Istio sees waypoint pods for
agw-waypoint.kubectl -n agentgateway-egress get pods \ -l gateway.networking.k8s.io/gateway-name=agw-waypointConfirm ServiceEntry waypoint labels.
kubectl -n agents get serviceentry httpbin-external -o yamlCheck ztunnel access logs and verify source and destination identities.
kubectl -n istio-system logs -l app=ztunnel --since=5m | \ grep 'src.namespace="agents"' | grep 'dst.service="httpbin.org"'
Optional: If Prometheus is installed in istio-system, query mTLS connection counters.
kubectl -n istio-system exec deploy/prometheus -- sh -c 'wget -qO- "http://localhost:9090/api/v1/query?query=sum(increase(istio_tcp_connections_opened_total%7Bsource_workload_namespace%3D%22agents%22%2Cdestination_workload_namespace%3D%22agentgateway-egress%22%2Cconnection_security_policy%3D%22mutual_tls%22%7D%5B5m%5D))"'Step 9: Use a real external provider
To adapt this demo for production egress from Istio-meshed agents, keep the same ambient pattern (client calls destination directly, Istio steers through the waypoint), then layer in these workflows.
Start with these baseline changes:
- Update the ServiceEntry host and AgentgatewayBackend host/port.
- Keep the client request pointed at the backend address, not the waypoint address.
- Match the waypoint
inner-httplistener port andagentgateway.dev/internal-portsvalue to the destination port that you want to route. - Configure TLS and provider authentication for the upstream.
Then apply egress workflows that are especially useful for meshed agents:
- Centralized credential injection for upstream AI providers, such as OAuth token exchange, Cross App Access, or JWT signing. This keeps provider credentials out of agent workloads and enforces one controlled identity path at egress.
- Route-level authentication and authorization, such as JWT authentication and authorization policies. This lets you decide which agents and users can call which external providers before traffic leaves the cluster.
- AI safety and spend controls, such as prompt guards and token-based rate limits. This reduces prompt injection risk, blocks sensitive content patterns, and prevents runaway token spend.
- Provider resilience and optimization, such as load balancing, failover, and content routing. This improves reliability and cost/performance by choosing the best model endpoint at request time.
- Egress observability and audit trails, such as LLM observability. This gives you per-request visibility into tokens, cost, and policy outcomes for compliance and incident response.
Cleanup
You can remove the resources that you created in this guide.kubectl -n agents delete networkpolicy allow-egress-to-istio-system-and-kube-dns --ignore-not-found
kubectl -n agentgateway-egress delete httproute httpbin-via-agw --ignore-not-found
kubectl -n agentgateway-egress delete agentgatewaybackend httpbin-backend --ignore-not-found
kubectl -n agents delete serviceentry httpbin-external --ignore-not-found
kubectl -n agentgateway-egress delete gateway agw-waypoint --ignore-not-found
kubectl -n agentgateway-egress delete agentgatewayparameters agw-waypoint-params --ignore-not-found
kubectl -n agents delete deploy curl --ignore-not-found
kubectl delete namespace agents agentgateway-egress --ignore-not-found