Step 1: Install ambient mesh

  1. 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.
  2. 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

  1. 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
      
  2. 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
      
  3. 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
      
  4. 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.

  1. Create a ServiceEntry that represents the external service httpbin.org. Because you label the ServiceEntry with the istio.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
      
  2. 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
       

  3. Send a request from the productpage to the external service. Verify that your response contains the server: istio-envoy and x-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"
       }
       

  4. Review the ztunnel logs.

    1. 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>
        
    2. 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
        
    3. 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.

  1. Create an AuthorizationPolicy that allows only GET requests to the httpbin.org service. You apply this policy to the egress gateway by using the targetRefs 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
      
  2. Send a POST request to httpbin.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
      
  3. 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.

  1. 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
      
  2. 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.

  1. Add targetPort: 443 to your ServiceEntry to instruct the egress gateway to use port 443 when routing traffic to httpbin.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
      
  2. 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
      
  3. Send a request to the httpbin.org external service and verify that traffic is sent to https://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