Control traffic with an egress gateway
Route all traffic through an egress gateway and enforce policies before traffic leaves your ambient mesh.
Step 1: Install ambient mesh
- Use the Solo distribution of Istio version 1.24.3 or later to set up an ambient mesh. You can set up the mesh by using the Gloo Operator or Helm.
- Add services to the ambient mesh. This guide walks you through an example for how to install the Bookinfo app and add it to your service mesh.
Step 2: Create an egress gateway
Create the
egress
namespace and add the following labels:istio.io/dataplane-mode: ambient
to add all pods in that namespace to the ambient mesh.istio.io/use-waypoint: waypoint
to 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-waypoint
Create a waypoint proxy that serves as the egress gateway. To configure your gateway as a waypoint proxy, you use the
istio-waypoint
GatewayClass. 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 EOF
Wait for the Gateway to be fully deployed.
kubectl -n istio-egress rollout status deployment/egress-waypoint
Example output:
deployment "egress-waypoint" successfully rolled out
Verify that your egress gateway pod is running.
kubectl get pods -n istio-egress
Example output:
NAME READY STATUS RESTARTS AGE egress-waypoint-5cb48fc696-mq99j 1/1 Running 0 87m
Step 3: 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
. Because you label the ServiceEntry with theistio.io/use-waypoint
label, all traffic to that service is automatically routed through your egress gateway.kubectl apply -f - <<EOF apiVersion: networking.istio.io/v1 kind: ServiceEntry metadata: annotations: 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 EOF
Verify that the ServiceEntry is attached to the waypoint by looking at its status.
kubectl get serviceentry httpbin.org -n istio-egress -o yaml
Example 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/WaypointBound
Send a request from the productpage to the external service. Verify that your response contains the
server: istio-envoy
andx-envoy-upstream-service-time
headers. 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.
Get the node that the egress gateway is deployed to. In the following example, the egress gateway is deployed on the
ip-10-0-6-27.us-east-2.compute.internal
node.kubectl get pods -n istio-egress -o wide
Example 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 ztunnel
Review 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-system
Example 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 4: 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
In this example, you learn how to use an Istio AuthorizationPolicy to restrict access to the external service for specific HTTP methods.
Create an AuthorizationPolicy that allows only
GET
requests to thehttpbin.org
service. You apply this policy to the egress gateway by using thetargetRefs
section.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"] EOF
Send a
POST
request tohttpbin.org
and 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 denied
Repeat the request. This time, you use the
GET
HTTP method that is allowed by the AuthorizationPolicy. Verify that your 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
In this example, you explore how to add headers to a request by using an Istio VirtualService.
Create a VirtualService that adds the
my-added-header: added-value
header to your request.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 EOF
Send a request to the
httpbin.org
external 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 your 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: 443
to your 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 EOF
Create 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 EOF
Send a request to the
httpbin.org
external 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