L7 waypoint egress
Deploy a waypoint proxy as an egress gateway and enforce L7 policies such as HTTP authorization, header manipulation, and TLS origination before traffic leaves your ambient mesh.
Set up L7 waypoint egress for your ambient mesh. This approach routes outbound traffic through an Envoy-based waypoint proxy, enabling HTTP-level authorization, header manipulation, rate limiting, and TLS origination. For a comparison of egress approaches, see the egress overview.
Before you begin
Ensure that DNS capture and IP autoallocation are enabled in your ambient mesh, which are required for ServiceEntry resources to receive stable virtual IPs. If you followed the quickstart guide, these are already configured. For Helm installations, add the following flags to your Helm install or upgrade command for the istiod Helm chart.
--set cni.ambient.dnsCapture=true --set pilot.env.PILOT_ENABLE_IP_AUTOALLOCATE=trueAdd services to the ambient mesh. The steps in this guide use the Bookinfo sample app services for example curl commands.
Step 1: Create an egress gateway
Create the
istio-egressnamespace and add the following labels:istio.io/dataplane-mode: ambientto add all pods in that namespace to the ambient mesh.istio.io/use-waypoint: egress-waypointto redirect all traffic through the egress waypoint proxy that you create in the next step.
kubectl create namespace istio-egress kubectl label ns istio-egress istio.io/dataplane-mode=ambient kubectl label ns istio-egress istio.io/use-waypoint=egress-waypointCreate a waypoint proxy that serves as the egress gateway. Use the
istio-waypointGatewayClass so that Istio configures and manages the gateway. The Gateway serves routes from all namespaces.kubectl apply -f - <<EOF apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: egress-waypoint namespace: istio-egress spec: gatewayClassName: istio-waypoint listeners: - name: mesh port: 15008 protocol: HBONE allowedRoutes: namespaces: from: All EOFWait for the Gateway to be fully deployed.
kubectl -n istio-egress rollout status deployment/egress-waypointExample output:
deployment "egress-waypoint" successfully rolled outVerify that the egress gateway pod is running.
kubectl get pods -n istio-egressExample output:
NAME READY STATUS RESTARTS AGE egress-waypoint-5cb48fc696-mq99j 1/1 Running 0 87m
Step 2: Route traffic through the egress gateway
Create an external service and configure your ambient mesh to route all traffic to that external service through the egress gateway that you set up earlier.
Create a ServiceEntry that represents the external service
httpbin.org. Label it withistio.io/use-waypointto route all traffic for that host through the egress gateway.kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: ServiceEntry metadata: labels: istio.io/use-waypoint: egress-waypoint name: httpbin.org namespace: istio-egress spec: hosts: - httpbin.org ports: - name: http number: 80 protocol: HTTP resolution: DNS EOFTip
Wildcard hostnames are not supported in ServiceEntry resources used with egress waypoints.
To create the ServiceEntry in a different namespace, label it for cross-namespace use.
Verify that the ServiceEntry is attached to the waypoint by checking its status.
kubectl get serviceentry httpbin.org -n istio-egress -o yamlExample output:
... status: addresses: - host: httpbin.org value: 240.240.0.2 - host: httpbin.org value: 2001:2::2 conditions: - lastTransitionTime: "2025-01-31T19:59:45.435933727Z" message: Successfully attached to waypoint egress/egress-waypoint reason: WaypointAccepted status: "True" type: istio.io/WaypointBoundSend a request from the productpage to the external service. Verify that the response contains the
server: istio-envoyandx-envoy-upstream-service-timeheaders. The presence of these headers verifies that traffic is routed through the egress gateway.kubectl -n bookinfo exec deploy/productpage-v1 -c curl -- curl -vik "httpbin.org/get"Example output:
... HTTP/1.1 200 OK date: Fri, 31 Jan 2025 20:00:01 GMT content-type: application/json content-length: 257 server: istio-envoy access-control-allow-origin: * access-control-allow-credentials: true x-envoy-upstream-service-time: 118 { "args": {}, "headers": { "Accept": "*/*", "Host": "httpbin.org", "User-Agent": "curl/7.83.1-DEV", "X-Amzn-Trace-Id": "Root=1-679d2bc1-4cfac8b64db7791a6c8fc55a" }, "origin": "18.XXX.XX.XX", "url": "http://httpbin.org/get" }Review the ztunnel logs to confirm the request transited the egress gateway.
Get the node that the egress gateway pod is deployed to. In the following example, the egress gateway is deployed on the
ip-10-0-6-27.us-east-2.compute.internalnode.kubectl get pods -n istio-egress -o wideExample output:
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES egress-waypoint-5cb48fc696-mq99j 1/1 Running 0 95m 10.0.2.127 ip-10-0-6-27.us-east-2.compute.internal <none> <none>Get the ztunnel instance that is deployed on the same node as the egress gateway.
kubectl get pods -n istio-system -o wide | grep ztunnelReview the logs of the ztunnel instance and verify that you can see a log entry for the request from the product page to the egress gateway. The presence of SPIFFE IDs for the source and destination confirms that traffic is secured via mutual TLS.
kubectl logs <ztunnel-instance> -n istio-systemExample output:
2025-02-20T16:52:48.094361Z info http access request complete src.addr=10.0.74.45:58668 src.workload="productpage-v1-7654b4b6d5-7c4l5" src.namespace="bookinfo" src.identity="spiffe://cluster1/ns/bookinfo/sa/bookinfo-productpage" dst.addr=10.0.77.91:15008 dst.hbone_addr=240.240.0.4:80 dst.service="httpbin.org" dst.workload="waypoint-865f5c9674-wfqwl" dst.namespace="egress" dst.identity="spiffe://cluster1/ns/istio-egress/sa/egress-waypoint" dst.cluster="cluster1" direction="outbound" method=GET path="/get" protocol=HTTP1 response_code=200 host="httpbin.org" user_agent="curl/7.83.1-dev" duration="13ms" 2025-02-20T16:52:48.095517Z info access connection complete src.addr=10.0.74.45:58668 src.workload="productpage-v1-7654b4b6d5-7c4l5" src.namespace="bookinfo" src.identity="spiffe://cluster1/ns/bookinfo/sa/bookinfo-productpage" dst.addr=10.0.77.91:15008 dst.hbone_addr=240.240.0.4:80 dst.service="httpbin.org" dst.workload="egress-waypoint-865f5c9674-wfqwl" dst.namespace="istio-egress" dst.identity="spiffe://cluster1/ns/istio-egress/sa/egress-waypoint" dst.cluster="cluster1" direction="outbound" bytes_sent=82 bytes_recv=493 duration="22ms"
Step 3: Apply an egress policy
Now that all traffic to the httpbin.org service is routed through the egress gateway, you can apply additional routing and security policies to it.
AuthorizationPolicy
Use an Istio AuthorizationPolicy to restrict access to the external service for specific HTTP methods.
Create an AuthorizationPolicy that allows only
GETrequests to thehttpbin.orgservice. Apply this policy to the egress gateway by using thetargetRefssection.kubectl apply -f - <<EOF apiVersion: security.istio.io/v1 kind: AuthorizationPolicy metadata: name: httpbin namespace: istio-egress spec: targetRefs: - kind: Gateway name: egress-waypoint group: gateway.networking.k8s.io action: ALLOW rules: - to: - operation: hosts: ["httpbin.org"] methods: ["GET"] paths: ["/get"] EOFSend a
POSTrequest tohttpbin.organd verify that this request is denied with a 403 HTTP response code.kubectl -n bookinfo exec deploy/productpage-v1 -c curl -- curl -vik "httpbin.org/post"Example output:
... HTTP/1.1 403 Forbidden content-length: 19 content-type: text/plain date: Thu, 20 Feb 2025 17:04:05 GMT server: istio-envoy RBAC: access deniedRepeat the request. This time, use the
GETHTTP method that is allowed by the AuthorizationPolicy. Verify that the request now succeeds.kubectl -n bookinfo exec deploy/productpage-v1 -c curl -- curl -vik "httpbin.org/get"Example output:
< HTTP/1.1 200 OK < date: Thu, 20 Feb 2025 17:05:10 GMT < content-type: application/json < content-length: 257 < server: istio-envoy < access-control-allow-origin: * < access-control-allow-credentials: true < x-envoy-upstream-service-time: 1070 { "args": {}, "headers": { "Accept": "*/*", "Host": "httpbin.org", "User-Agent": "curl/7.83.1-DEV", "X-Amzn-Trace-Id": "Root=1-679d2bc1-4cfac8b64db7791a6c8fc55a" }, "origin": "18.XXX.XX.XX", "url": "http://httpbin.org/get" }
Header manipulation
Add headers to a request centrally at the egress gateway by using an Istio VirtualService.
Create a VirtualService that adds the
my-added-header: added-valueheader to all requests tohttpbin.org.kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: VirtualService metadata: name: httpbin namespace: istio-egress spec: hosts: - httpbin.org http: - route: - destination: host: httpbin.org headers: request: add: my-added-header: added-value EOFSend a request to the
httpbin.orgexternal service and verify that you see the added request header.kubectl -n bookinfo exec deploy/productpage-v1 -c curl -- curl -vik "httpbin.org/get"Example output:
< HTTP/1.1 200 OK ... content-type: application/json content-length: 296 server: istio-envoy access-control-allow-origin: * access-control-allow-credentials: true x-envoy-upstream-service-time: 99 { "args": {}, "headers": { "Accept": "*/*", "Host": "httpbin.org", "My-Added-Header": "added-value", "User-Agent": "curl/7.83.1-DEV", "X-Amzn-Trace-Id": "Root=1-679d3bc5-1dd72be652986bb53d92b244" }, "origin": "18.XXX.XX.XX", "url": "http://httpbin.org/get" }
TLS origination
By default, all traffic within the ambient mesh is secured via mutual TLS. However, when routing traffic through an egress gateway, the egress gateway terminates the TLS connection and forwards the unencrypted request to the external service.
With TLS origination, you instruct the egress gateway to re-encrypt the request before it is forwarded to the external service.
Add
targetPort: 443to the ServiceEntry to instruct the egress gateway to use port 443 when routing traffic tohttpbin.org.kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: ServiceEntry metadata: name: httpbin.org namespace: istio-egress spec: hosts: - httpbin.org ports: - number: 80 name: http protocol: HTTP targetPort: 443 resolution: DNS EOFCreate a DestinationRule to instruct the egress gateway to originate a TLS connection when routing traffic to
httpbin.org.kubectl apply -f- <<EOF apiVersion: networking.istio.io/v1 kind: DestinationRule metadata: name: httpbin.org-tls namespace: istio-egress spec: host: httpbin.org trafficPolicy: tls: mode: SIMPLE EOFSend a request to the
httpbin.orgexternal service and verify that traffic is sent tohttps://httpbin.org/get.kubectl -n bookinfo exec deploy/productpage-v1 -c curl -- curl -vik "httpbin.org/get"Example output:
< HTTP/1.1 200 OK ... content-type: application/json content-length: 297 server: istio-envoy access-control-allow-origin: * access-control-allow-credentials: true x-envoy-upstream-service-time: 303 { "args": {}, "headers": { "Accept": "*/*", "Host": "httpbin.org", "My-Added-Header": "added-value", "User-Agent": "curl/7.83.1-DEV", "X-Amzn-Trace-Id": "Root=1-679d4675-146cc1027fba135708ae3fef" }, "origin": "18.XXX.XX.XX", "url": "https://httpbin.org/get" }
Cleanup
You can optionally remove the resources that you created in this guide.
kubectl delete authorizationpolicy httpbin -n istio-egress
kubectl delete virtualservice httpbin -n istio-egress
kubectl delete serviceentry httpbin.org -n istio-egress
kubectl delete destinationrule httpbin.org-tls -n istio-egress
kubectl delete gateway egress-waypoint -n istio-egress
kubectl delete namespace istio-egress