All Products
Search
Document Center

Alibaba Cloud Service Mesh:Manage egress traffic with an egress traffic policy

Last Updated:Mar 10, 2026

When workloads in a service mesh need controlled access to external services, manually creating and linking ServiceEntry, VirtualService, Gateway, and Destination resources is complex and error-prone. Service Mesh (ASM) 1.16.4 and later provides the ASMEgressTrafficPolicy custom resource definition (CRD), which consolidates these resources into a single definition. With one ASMEgressTrafficPolicy resource, you specify which workloads can reach which external services, over which protocols, and through which egress gateway.

Use cases

  • Security compliance: Route all outbound traffic through dedicated egress gateway nodes for policy enforcement and auditing. This suits environments where compliance rules require every external connection to be logged and inspectable.

  • Fine-grained access control: Combine egress traffic policies with AuthorizationPolicy resources to allow or deny specific HTTP methods, paths, or source namespaces at the egress gateway.

How it works

When you apply an ASMEgressTrafficPolicy, ASM automatically generates the ServiceEntry, VirtualService, Gateway, and Destination configurations that redirect traffic from the sidecar proxy to the egress gateway, which then forwards it to the external service.

Architecture diagram showing traffic flow from application pod through sidecar proxy to egress gateway and then to external service

Traffic between mesh components follows these communication patterns:

SegmentProtocolDetails
Sidecar-to-sidecar, sidecar-to-gatewaymutual TLS (mTLS)Enabled by default. ASM manages the certificates.
Application-to-sidecarPlaintext (recommended)Plaintext lets the sidecar inspect Layer 7 traffic and support routing and observability. If the application sends HTTPS directly, only Layer 4 (TCP-level) capabilities are available.
Gateway-to-external servicePlaintext or HTTPSConfigurable per egress rule. The gateway can also upgrade HTTP to HTTPS before forwarding.
ASMEgressTrafficPolicy does not support HTTP plaintext communication between the sidecar proxy and the egress gateway. mTLS is always used on this segment.

Limitations

ASMEgressTrafficPolicy does not support:

  • Proportional (weighted) routing of egress traffic

  • Initiating mTLS from the egress gateway to external services

  • Other advanced configurations available through native Istio resources

For these capabilities, configure the egress gateway manually. See Configure an egress gateway to route all outbound traffic in ASM.

Prerequisites

Set up the environment

Complete these one-time setup steps before creating egress traffic policies.

Set the outbound traffic policy to REGISTRY_ONLY

  1. Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.

  2. On the Mesh Management page, click the name of the ASM instance. In the left-side navigation pane, choose Data Plane Component Management > Sidecar Proxy Setting.

  3. On the global tab, set External Access Policy to REGISTRY_ONLY, and then click Update Settings.

With this setting, workloads can only access external services explicitly defined in the mesh registry.

Create the istio-egress namespace

  1. Create the istio-egress namespace. See Manage global namespaces.

  2. On the Global namespace page, click Sync Automatic Sidecar Injection to Kubernetes Cluster to sync the namespace to the ACK cluster.

Create an egress gateway

Create an egress gateway named egressgateway-a with the following port mappings:

ProtocolPort
HTTP80
HTTPS443
HTTPS444

Enable Support two-way TLS authentication. For steps, see Create an egress gateway.

After creation, verify that the generated YAML contains the following spec field. Add it if missing:

spec:
  podLabels:
    security.istio.io/tlsMode: istio

Deploy test workloads

Deploy two services to test egress policies: sleep-a in the mytest namespace (with sidecar injection) and nginx in the default namespace.

  1. Create the mytest namespace and enable automatic sidecar proxy injection. See Manage global namespaces.

  2. Save the following YAML to test.yaml:

    Expand to view YAML file

       apiVersion: v1
       kind: Service
       metadata:
         name: sleep-a
         namespace: mytest
         labels:
           app: sleep-a
           service: sleep-a
       spec:
         ports:
         - port: 80
           name: http
         selector:
           app: sleep-a
       ---
       apiVersion: apps/v1
       kind: Deployment
       metadata:
         name: sleep-a
         namespace: mytest
       spec:
         replicas: 1
         selector:
           matchLabels:
             app: sleep-a
         template:
           metadata:
             labels:
               app: sleep-a
           spec:
             terminationGracePeriodSeconds: 0
             containers:
             - name: sleep
               image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/curl:asm-sleep
               command: ["/bin/sleep", "infinity"]
               imagePullPolicy: IfNotPresent
               volumeMounts:
               - mountPath: /etc/sleep/tls
                 name: secret-volume
             volumes:
             - name: secret-volume
               secret:
                 secretName: sleep-secret
                 optional: true
       ---
       apiVersion: apps/v1
       kind: Deployment
       metadata:
         labels:
           app: nginx
         name: nginx
         namespace: default
       spec:
         replicas: 1
         selector:
           matchLabels:
             app: nginx
         template:
           metadata:
             labels:
               app: nginx
           spec:
             containers:
             - image: registry-cn-hangzhou.ack.aliyuncs.com/ack-demo/nginx:1.27.0
               imagePullPolicy: Always
               name: nginx
  3. Deploy both services:

       kubectl apply -f test.yaml
  4. Verify that external access is blocked: Both commands return 502, confirming that the REGISTRY_ONLY policy blocks outbound traffic.

       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" http://www.httpbin.org
       kubectl -n default exec deployment/nginx -- curl -s -o /dev/null -w "%{http_code}\n" http://www.httpbin.org

Route HTTP traffic through the egress gateway

ASMEgressTrafficPolicy supports only mTLS for communication between the sidecar proxy and the egress gateway. HTTP plaintext communication to the egress gateway is not supported because:

  • Traffic management is implemented in sidecar proxies on the client side and does not require egress gateways.

  • Observability does not rely on egress gateways.

  • Security capabilities depend on egress gateways. However, without mTLS, all authorization capabilities based on client identities are unavailable. In this case, egress gateways can only deny all requests indiscriminately.

The following steps configure sleep-a in the mytest namespace to access http://www.httpbin.org through the egress gateway using mTLS.

Traffic flow: sleep-a sidecar sends HTTP over mTLS to egress gateway, which forwards HTTP to httpbin.org

Create the egress traffic policy

ASM console

  1. Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.

  2. On the Mesh Management page, click the name of the ASM instance. In the left-side navigation pane, choose ASM Gateways > Egress Gateway.

  3. Click the gateway name to open the Gateway overview page. Click Outbound Traffic Policy on the left. Configure parameters as shown in the following figure.

Console configuration for HTTP egress traffic policy with mTLS

kubectl

  1. Save the following YAML to egress-by-egressgateway.yaml: For a complete field reference, see ASMEgressTrafficPolicy CRD.

       apiVersion: istio.alibabacloud.com/v1
       kind: ASMEgressTrafficPolicy
       metadata:
         name: egress-by-egressgateway  # Format: egress-by-{egress gateway name}
         namespace: istio-egress         # Fixed value
       spec:
         byEgressGateway:
           name: egressgateway
         egressRules:
         - from:
           - namespace: mytest
             workloadSelector:
               app: sleep-a
           to:
           - name: httpbin-service-http
             hosts:
             - www.httpbin.org  # Multiple hosts must resolve to the same IP addresses
             - httpbin.org
             port:
               name: http
               number: 80
               protocol: HTTP
             byEgressGateway:
               port: 80        # Sidecar -> port 80 -> Gateway -> port 80 -> httpbin.org
  2. Apply the policy:

       kubectl apply -f egress-by-egressgateway.yaml

Verify the policy

  1. Confirm that nginx in the default namespace is still blocked: Expected output: 502

       kubectl -n default exec deployment/nginx -- curl -s -o /dev/null -w "%{http_code}\n" http://www.httpbin.org
  2. Confirm that sleep-a in the mytest namespace can access the external service: Expected output: 200

       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" http://www.httpbin.org
  3. Delete the policy and confirm that access is revoked: Expected output: 502. This confirms the egress traffic policy was controlling the access.

       kubectl -n istio-egress delete ASMEgressTrafficPolicy egress-by-egressgateway
       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" http://www.httpbin.org

Route HTTPS traffic through the egress gateway

Two solutions are available for routing HTTPS egress traffic. Choose based on where TLS is handled:

SolutionSidecar-to-gatewayGateway-to-externalPort flowWhen to use
Solution 1: mTLS + HTTPS upgrade (recommended)mTLSHTTPS (upgraded from HTTP at gateway)Sidecar -> 80 -> Gateway -> 443 -> ExternalApplications send HTTP. The gateway handles TLS. Simpler application code.
Solution 2: HTTPS passthroughmTLSHTTPS (passthrough)Sidecar -> 444 -> Gateway -> 443 -> ExternalApplications send HTTPS directly.

Solution 1: mTLS with HTTPS upgrade (recommended)

The egress gateway upgrades HTTP requests to HTTPS before forwarding them to the external service. Applications send plaintext HTTP, keeping TLS management at the gateway level.

Traffic flow: sleep-a sends HTTP, sidecar uses mTLS to gateway, gateway upgrades to HTTPS for httpbin.org

Create the egress traffic policy

ASM console
  1. Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.

  2. On the Mesh Management page, click the name of the ASM instance. In the left-side navigation pane, choose ASM Gateways > Egress Gateway.

  3. Click the gateway name to open the Gateway overview page, and then click Outbound Traffic Policy on the left. Configure parameters as shown in the following figure.

Console configuration for HTTPS egress traffic policy with mTLS and HTTPS upgrade
kubectl
  1. Save the following YAML to egress-by-egressgateway.yaml. This configuration includes the httpsUpgrade field, which tells the gateway to upgrade HTTP to HTTPS: For a complete field reference, see ASMEgressTrafficPolicy CRD.

       apiVersion: istio.alibabacloud.com/v1
       kind: ASMEgressTrafficPolicy
       metadata:
         name: egress-by-egressgateway  # Format: egress-by-{egress gateway name}
         namespace: istio-egress         # Fixed value
       spec:
         byEgressGateway:
           name: egressgateway
         egressRules:
         - from:
           - namespace: mytest
             workloadSelector:
               app: sleep-a
           to:
           - name: httpbin-service-http
             hosts:
             - www.httpbin.org  # Multiple hosts must resolve to the same IP addresses
             - httpbin.org
             port:
               name: http
               number: 80
               protocol: HTTP
             byEgressGateway:
               port: 80        # Sidecar -> port 80 -> Gateway -> port 80 -> httpbin.org (HTTP)
             httpsUpgrade:
               enabled: true   # Enable HTTPS upgrade at the gateway
               port: 443       # Sidecar -> port 80 -> Gateway -> port 443 -> httpbin.org (HTTPS)
  2. Apply the policy:

       kubectl apply -f egress-by-egressgateway.yaml

Verify the policy

  1. Confirm HTTP access from sleep-a: Expected output: 200

       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" http://httpbin.org
  2. Verify the HTTPS upgrade. Request the /anything endpoint, which echoes the request URL: Expected output: The url field starts with https, confirming that the gateway upgraded the request to HTTPS before forwarding.

       kubectl -n mytest exec deployment/sleep-a -- sh -c "curl -s http://httpbin.org/anything | grep url"
       "url": "https://httpbin.org/anything"
  3. Confirm HTTPS access from sleep-a: Expected output: 200

       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" https://www.httpbin.org
  4. Confirm that nginx in the default namespace is still blocked: HTTP returns 502. HTTPS returns a connection refused error. To see why, check the sidecar proxy logs: The log shows "upstream_cluster":"BlackHoleCluster", meaning the request was routed to a cluster that drops all traffic -- confirming the policy blocks access for unauthorized workloads.

       # HTTP access
       kubectl -n default exec deployment/nginx -- curl -s -o /dev/null -w "%{http_code}\n" http://www.httpbin.org
       # HTTPS access
       kubectl -n default exec deployment/nginx -- curl -s -o /dev/null -w "%{http_code}\n" https://www.httpbin.org
       kubectl -n default logs -f deployment/nginx -c istio-proxy --tail=1
  5. Delete the policy and confirm that access is revoked for sleep-a: HTTP returns 502. HTTPS returns a connection refused error.

       kubectl -n istio-egress delete ASMEgressTrafficPolicy egress-by-egressgateway
       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" http://www.httpbin.org
       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" https://www.httpbin.org

Solution 2: HTTPS passthrough

The sidecar proxy forwards HTTPS traffic to the egress gateway through mTLS, and the gateway passes the HTTPS connection through to the external service without terminating TLS.

Traffic flow: sleep-a sends HTTPS, sidecar uses mTLS to gateway, gateway passes HTTPS through to httpbin.org

Create the egress traffic policy

ASM console
  1. Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.

  2. On the Mesh Management page, click the name of the ASM instance. In the left-side navigation pane, choose ASM Gateways > Egress Gateway.

  3. Click the gateway name to open the Gateway overview page, and then click Outbound Traffic Policy on the left. Configure parameters as shown in the following figure.

Console configuration for HTTPS passthrough egress traffic policy
kubectl
  1. Save the following YAML to egress-by-egressgateway.yaml: For a complete field reference, see ASMEgressTrafficPolicy CRD.

       apiVersion: istio.alibabacloud.com/v1
       kind: ASMEgressTrafficPolicy
       metadata:
         name: egress-by-egressgateway  # Format: egress-by-{egress gateway name}
         namespace: istio-egress         # Fixed value
       spec:
         byEgressGateway:
           name: egressgateway
         egressRules:
         - from:
           - namespace: mytest
             workloadSelector:
               app: sleep-a
           to:
           - name: httpbin-service-https
             hosts:
             - www.httpbin.org
             - httpbin.org
             port:
               name: https
               number: 443
               protocol: HTTPS
             byEgressGateway:
               port: 444   # Uses the HTTPS 444 port defined during gateway creation
  2. Apply the policy:

       kubectl apply -f egress-by-egressgateway.yaml

Verify the policy

  1. Confirm HTTP access from sleep-a: Expected output: 200

       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" http://httpbin.org
  2. Verify that the request reaches the external service over HTTPS: Expected output: The url field starts with https, confirming that the external service received an HTTPS request.

       kubectl -n mytest exec deployment/sleep-a -- sh -c "curl -s http://httpbin.org/anything | grep url"
       "url": "https://httpbin.org/anything"
  3. Confirm direct HTTPS access from sleep-a: Expected output: 200

       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" https://www.httpbin.org
  4. Confirm that nginx in the default namespace is still blocked: HTTP returns 502. HTTPS returns a connection refused error.

       kubectl -n default exec deployment/nginx -- curl -s -o /dev/null -w "%{http_code}\n" http://www.httpbin.org
       kubectl -n default exec deployment/nginx -- curl -s -o /dev/null -w "%{http_code}\n" https://www.httpbin.org
  5. Delete the policy and confirm that access is revoked: HTTP returns 502. HTTPS returns a connection refused error.

       kubectl -n istio-egress delete ASMEgressTrafficPolicy egress-by-egressgateway
       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" http://www.httpbin.org
       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" https://www.httpbin.org

Route TCP traffic through the egress gateway

When the application initiates TLS directly and the mesh only needs to route the encrypted stream, configure the egress traffic policy with the TCP protocol.

Traffic flow: sleep-a sends TCP traffic through sidecar and egress gateway to external service

Create the egress traffic policy

ASM console

  1. Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.

  2. On the Mesh Management page, click the name of the ASM instance. In the left-side navigation pane, choose ASM Gateways > Egress Gateway.

  3. Click the gateway name to open the Gateway overview page, and then click Outbound Traffic Policy on the left. Configure parameters as shown in the following figure.

Console configuration for TCP egress traffic policy

kubectl

  1. Save the following YAML to egress-by-egressgateway.yaml:

       apiVersion: istio.alibabacloud.com/v1
       kind: ASMEgressTrafficPolicy
       metadata:
         name: egress-by-egressgateway
         namespace: istio-egress
       spec:
         byEgressGateway:
           name: egressgateway
         egressRules:
         - from:
           - namespace: mytest
             workloadSelector:
               app: sleep-a
           to:
           - byEgressGateway: {}
             hosts:
             - www.alibabacloud.com
             name: aliyun-service-tcp
             port:
               name: tcp
               number: 443
               protocol: TCP
  2. Apply the policy:

       kubectl apply -f egress-by-egressgateway.yaml

Verify the policy

  1. Confirm that HTTP access from sleep-a to www.alibabacloud.com returns an error. TCP mode routes raw TCP streams and does not handle HTTP protocol negotiation: Expected output: 502

       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" http://www.alibabacloud.com
  2. Confirm that HTTPS access from sleep-a succeeds. The application initiates TLS, and the gateway passes the TCP stream through: Expected output: 200

       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" https://www.alibabacloud.com
  3. Confirm that nginx in the default namespace is blocked: HTTP returns 502. HTTPS returns a connection refused error. The sidecar proxy logs show "upstream_cluster":"BlackHoleCluster".

       kubectl -n default exec deployment/nginx -- curl -s -o /dev/null -w "%{http_code}\n" http://www.alibabacloud.com
       kubectl -n default exec deployment/nginx -- curl -s -o /dev/null -w "%{http_code}\n" https://www.alibabacloud.com
  4. Delete the policy and confirm that access is revoked: HTTP returns 502. HTTPS returns a connection refused error.

       kubectl -n istio-egress delete ASMEgressTrafficPolicy egress-by-egressgateway
       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" http://www.alibabacloud.com
       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" https://www.alibabacloud.com

Add fine-grained access control with an authorization policy

Combine an AuthorizationPolicy with the egress traffic policy to control access at the egress gateway. The following example denies POST requests from the mytest namespace to httpbin.org:

kind: AuthorizationPolicy
apiVersion: security.istio.io/v1beta1
metadata:
  name: sleep-a-egress-www-httpbin-org
  namespace: istio-system
spec:
  action: DENY
  rules:
    - to:
        - operation:
            hosts:
            - www.httpbin.org
            - httpbin.org
            methods:
              - POST
      from:
        - source:
            namespaces: ["mytest"]
  selector:
    matchLabels:
      istio: egressgateway-a

After applying this policy, POST requests from sleep-a to httpbin.org return RBAC: access. GET requests are unaffected.

For more examples, see Configure authorization policies for HTTP requests.