All Products
Search
Document Center

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

Last Updated:Mar 11, 2026

Organizations with strict security requirements often need all outbound traffic from their service mesh to flow through a controlled set of gateway nodes for auditing, policy enforcement, and monitoring. The ASMEgressTrafficPolicy CustomResourceDefinition (CRD) lets you define which workloads can reach which external services through an egress gateway -- without changes to application code. Behind the scenes, it generates the underlying ServiceEntry, VirtualService, Gateway, DestinationRule, and Sidecar resources automatically.

This topic walks through two scenarios:

  • Scenario 1: Allow a specific workload to access an external HTTP service while all other workloads remain blocked.

  • Scenario 2: Upgrade HTTP requests to HTTPS at the egress gateway, and allow direct HTTPS access.

A final section shows how to layer an AuthorizationPolicy on the egress gateway to deny specific HTTP methods.

How egress traffic flows

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

Traffic from an application pod is intercepted by its sidecar proxy. Based on the egress traffic policy, the sidecar routes the request to the egress gateway instead of sending it directly to the internet. The egress gateway then forwards the request to the external service.

The flow is: application container -> sidecar proxy -> egress gateway -> external service.

The ASMEgressTrafficPolicy CRD controls this routing. Workloads that do not match any policy rule are routed to BlackHoleCluster and dropped.

Prerequisites

Before you begin, make sure that you have:

Set up the environment

Complete the following steps before configuring egress traffic policies.

Set the outbound traffic policy to REGISTRY_ONLY

By default, sidecar proxies allow all outbound traffic (ALLOW_ANY mode). In this mode, egress traffic policies have no effect because traffic flows directly to external services without going through the egress gateway.

Switch to REGISTRY_ONLY mode so that sidecar proxies only allow traffic to explicitly registered services. This makes the egress gateway the only path to external services.

  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 Dataplane Component Management > Sidecar Proxy Setting.

  3. On the global tab, set Outbound Traffic Policy to REGISTEY_ONLY, and then click Update Settings.

Create the istio-egress namespace

The istio-egress namespace is a fixed namespace that all ASMEgressTrafficPolicy resources must use.

  1. Create a namespace named istio-egress. See Manage global namespaces.

  2. On the Global Namespace page, click Sync Automatic Sidecar Injection to Kubernetes Cluster to synchronize the namespace to the ACK cluster that is added to the ASM instance.

Create an egress gateway

Create an egress gateway named egressgateway-a in the ASM console. In the Port Mapping section, add the following ports:

ProtocolPort
HTTP80
HTTPS443
HTTPS444

Select Support two-way TLS authentication for the egress gateway. See Create an egress gateway.

After the gateway is created, verify that the YAML contains the following in the spec section. Add it manually if needed:

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

Deploy test workloads

Deploy two test services to verify egress policy behavior: sleep-a in the mytest namespace and nginx in the default namespace.

Create the mytest namespace

Create a namespace named mytest for the ASM instance and enable automatic sidecar proxy injection. See Manage global namespaces.

Deploy sleep-a and nginx

  1. Create a file named test.yaml with the following content:

    View test.yaml

       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: curlimages/curl
               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:
         progressDeadlineSeconds: 600
         replicas: 1
         revisionHistoryLimit: 10
         selector:
           matchLabels:
             app: nginx
         strategy:
           rollingUpdate:
             maxSurge: 25%
             maxUnavailable: 25%
           type: RollingUpdate
         template:
           metadata:
             creationTimestamp: null
             labels:
               app: nginx
           spec:
             containers:
             - image: nginx
               imagePullPolicy: Always
               name: nginx
               resources: {}
               terminationMessagePath: /dev/termination-log
               terminationMessagePolicy: File
             dnsPolicy: ClusterFirst
             restartPolicy: Always
             schedulerName: default-scheduler
             securityContext: {}
             terminationGracePeriodSeconds: 30
  2. Deploy the services in the ACK cluster:

       kubectl apply -f test.yaml

Verify that outbound traffic is blocked

Run the following commands to confirm that both services cannot access http://www.httpbin.org:

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

Both commands return 502, confirming that REGISTRY_ONLY mode blocks all outbound traffic by default.

Scenario 1: Allow a specific workload to access an external HTTP service

In this scenario, only the sleep-a workload in the mytest namespace can access http://www.httpbin.org through the egress gateway. All other workloads remain blocked.

Apply the egress traffic policy

  1. Create a file named egress-by-egressgateway-a.yaml with the following content: Key fields: For the full CRD reference, see CRD fields in an egress traffic policy.

    FieldDescription
    metadata.nameMust follow the format egress-by-{egress gateway name}
    metadata.namespaceMust be istio-egress
    from[].namespace + workloadSelectorSelects which workloads the rule applies to
    to[].hostsExternal hosts to allow. Multiple hosts must resolve to the same IP addresses after DNS resolution
    to[].byEgressGateway.portThe port on the egress gateway that receives the traffic
       apiVersion: istio.alibabacloud.com/v1
       kind: ASMEgressTrafficPolicy
       metadata:
         name: egress-by-egressgateway-a  # Format: egress-by-{egress gateway name}
         namespace: istio-egress          # Fixed namespace
       spec:
         byEgressGateway:
           name: egressgateway-a
         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        # Traffic path: sidecar proxy -> gateway (port 80) -> httpbin.org (port 80)
  2. Apply the policy:

       kubectl apply -f egress-by-egressgateway-a.yaml

Verify the policy

  1. Test that the nginx workload in the default namespace is still blocked: Expected result: 502 -- access denied.

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

       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 verify that sleep-a loses access: Expected result: 502 -- the policy is no longer in effect.

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

Scenario 2: Upgrade HTTP to HTTPS and allow direct HTTPS access

This scenario adds two capabilities on top of Scenario 1:

  • HTTP-to-HTTPS upgrade: The egress gateway automatically upgrades HTTP requests from sleep-a to HTTPS before forwarding them to the external service. This lets legacy applications send plain HTTP while the mesh handles TLS origination.

  • Direct HTTPS access: sleep-a can also send HTTPS requests directly to https://www.httpbin.org through the egress gateway.

Apply the egress traffic policy

  1. Create egress-by-egressgateway-a.yaml with the following content: Compared to Scenario 1, this YAML adds:

    If httpsUpgrade.enabled is set to false, the httpsUpgrade.port value has no effect.
    AdditionPurpose
    httpsUpgrade.enabled: trueEnables HTTP-to-HTTPS upgrade at the egress gateway
    httpsUpgrade.port: 443Specifies the HTTPS port to forward upgraded requests to
    httpbin-service-https blockAllows direct HTTPS traffic through gateway port 444
       apiVersion: istio.alibabacloud.com/v1
       kind: ASMEgressTrafficPolicy
       metadata:
         name: egress-by-egressgateway-a  # Format: egress-by-{egress gateway name}
         namespace: istio-egress          # Fixed namespace
       spec:
         byEgressGateway:
           name: egressgateway-a
         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        # Traffic path: sidecar proxy -> gateway (port 80) -> httpbin.org (port 80)
             httpsUpgrade:
               enabled: true    # Upgrade HTTP to HTTPS at the egress gateway
               port: 443        # Traffic path: sidecar proxy -> gateway (port 80) -> httpbin.org (port 443)
           - name: httpbin-service-https
             hosts:
             - www.httpbin.org
             - httpbin.org
             port:
               name: https
               number: 443
               protocol: HTTPS
             byEgressGateway:
               port: 444        # Uses port 444 configured in the "Create an egress gateway" section
  2. Apply the policy:

       kubectl apply -f egress-by-egressgateway-a.yaml

Verify the policy

Test sleep-a (mytest namespace)

  1. Verify HTTP access: Expected result: 200.

       kubectl -n mytest exec deployment/sleep-a -- curl -s -o /dev/null -w "%{http_code}\n" http://httpbin.org
  2. Confirm the HTTP-to-HTTPS upgrade by sending an HTTP request and checking the response URL: Expected output: The url starts with https, confirming that the egress gateway upgraded the request before forwarding it.

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

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

Test nginx (default namespace)

  1. Verify that HTTP access is blocked: Expected result: 502.

       kubectl -n default exec deployment/nginx -- curl -s -o /dev/null -w "%{http_code}\n" http://www.httpbin.org
  2. Verify that HTTPS access is blocked: Expected result: the request is rejected.

       kubectl -n default exec deployment/nginx -- curl -s -o /dev/null -w "%{http_code}\n" https://www.httpbin.org
  3. To confirm the rejection, inspect the sidecar proxy logs for the nginx workload: Expected output (formatted for readability): The upstream_cluster value BlackHoleCluster indicates that the request was dropped because no matching egress rule exists for the nginx workload.

       kubectl -n default logs -f deployment/nginx -c istio-proxy --tail=1
       {
         "upstream_cluster": "BlackHoleCluster",
         "response_code": "0",
         "response_flags": "UH",
         ...
       }

Clean up

Delete the policy and confirm that sleep-a loses access:

kubectl -n istio-egress delete ASMEgressTrafficPolicy egress-by-egressgateway-a
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

Expected results:

  • HTTP returns 502.

  • HTTPS is rejected.

This confirms the egress traffic policy was the only path allowing outbound access.

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

Deny specific HTTP methods with an authorization policy

Egress traffic policies control which workloads can reach which external hosts. For finer-grained control -- such as blocking specific HTTP methods -- layer an AuthorizationPolicy on the egress gateway.

The following example denies POST requests from the mytest namespace to httpbin.org while allowing GET requests:

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 www.httpbin.org return RBAC: access (denied by the authorization policy).

  • GET requests from sleep-a to www.httpbin.org continue to return 200.

Security considerations

The egress traffic policy enforces routing through the egress gateway at the mesh layer via sidecar proxies. Keep the following limitations in mind:

  • Sidecar bypass: If a workload bypasses its sidecar proxy (for example, by running a privileged container or manipulating iptables rules), it can reach external services directly without traversing the egress gateway. To prevent this, combine egress traffic policies with Kubernetes NetworkPolicies or cloud firewall rules that deny direct outbound traffic from application nodes.

  • Scope of enforcement: The policy applies only to workloads with sidecar injection enabled. Pods in namespaces without sidecar injection are not subject to egress traffic policies.