All Products
Search
Document Center

Alibaba Cloud Service Mesh:Route all outbound traffic through an egress gateway

Last Updated:Mar 11, 2026

By default, pods in an ASM instance can reach any external endpoint. An egress gateway gives you a single exit point for all outbound traffic, where you can enforce security policies, log external requests, and gain full observability -- without changing application code.

Important

This topic configures the underlying ASM resources (ServiceEntry, Gateway, VirtualService) that redirect outbound requests to the egress gateway. The setup is more involved than the declarative approach. Read Use ASMEgressTrafficPolicy to manage egress traffic first -- it covers a simpler, CRD-based method. Use the manual approach below only when ASMEgressTrafficPolicy does not meet your requirements.

When to use an egress gateway

  • Centralized security control -- Apply security policies at one exit point instead of configuring every sidecar.

  • Observability -- Capture access logs for all outbound traffic in one place for monitoring and troubleshooting.

  • Traffic routing -- Route and manage all outbound traffic through a single, controlled gateway.

How it works

Configuration process
  1. A ServiceEntry registers the external host (for example, aliyun.com) in the mesh's service registry.

  2. A VirtualService with two route rules directs traffic from the sidecar to the egress gateway, then from the egress gateway to the external host.

  3. An Istio Gateway on the egress gateway accepts the inbound traffic on the designated port.

All outbound requests from application pods flow through the egress gateway, where security policies are enforced before the traffic leaves the mesh.

Prerequisites

Before you begin, make sure that you have:

Step 1: Deploy a sample application

Deploy the sleep application -- a minimal pod with curl -- to test outbound connectivity throughout this tutorial.

  1. Save the following YAML as sleep.yaml:

    Expand to view sleep.yaml

       apiVersion: v1
       kind: ServiceAccount
       metadata:
         name: sleep
       ---
       apiVersion: v1
       kind: Service
       metadata:
         name: sleep
         labels:
           app: sleep
           service: sleep
       spec:
         ports:
         - port: 80
           name: http
         selector:
           app: sleep
       ---
       apiVersion: apps/v1
       kind: Deployment
       metadata:
         name: sleep
       spec:
         replicas: 1
         selector:
           matchLabels:
             app: sleep
         template:
           metadata:
             labels:
               app: sleep
           spec:
             terminationGracePeriodSeconds: 0
             serviceAccountName: sleep
             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
  2. Apply the manifest:

       kubectl apply -f sleep.yaml
  3. Verify outbound connectivity: Replace <sleep-pod-name> with the actual pod name. To find it, run kubectl get pod -n default. Expected output: A 301 confirms the pod can reach aliyun.com. The web server redirects HTTP to HTTPS -- this is expected.

       kubectl exec -it <sleep-pod-name> -- curl -I aliyun.com
       HTTP/1.1 301 Moved Permanently
       server: envoy
       ...
       location: https://aliyun.com/
Note

By default, ASM uses the ALLOW_ANY outbound traffic policy, which lets pods reach any external host. Under this policy, ASM cannot enforce access control or provide observability capabilities for external services. The next step switches to REGISTRY_ONLY so that only registered external services are reachable.

Step 2 (optional): Switch the outbound traffic policy to REGISTRY_ONLY

The REGISTRY_ONLY policy blocks access to any external host without a ServiceEntry. Sidecar proxies deny unregistered outbound traffic, providing stronger security than the default ALLOW_ANY policy.

Note
  • If you enable REGISTRY_ONLY without creating a ServiceEntry, all external access is blocked.

  • If you keep ALLOW_ANY without a ServiceEntry, pods can reach external services, but traffic does not flow through the egress gateway.

  1. Log on to the ASM console.

  2. In the left-side navigation pane, choose Service Mesh > Mesh Management.

  3. On the Mesh Management page, click the name of the ASM instance.

  4. In the left-side navigation pane, choose Data Plane Component Management > Sidecar Proxy Setting.

  5. On the Sidecar Proxy Setting page, click the global tab, click Outbound Traffic Policy, set External Access Policy to REGISTRY_ONLY, and then click Update Settings.

  6. Verify that external access is blocked: Expected output: A 502 confirms that the sidecar proxy is blocking access to unregistered external hosts.

       kubectl exec -it <sleep-pod-name> -- curl -I aliyun.com
       HTTP/1.1 502 Bad Gateway
       server: envoy

Step 3: Create a ServiceEntry for the external service

A ServiceEntry registers an external host in the mesh's service registry so that sidecar proxies recognize and route traffic to it.

  1. Log on to the ASM console.

  2. In the left-side navigation pane, choose Service Mesh > Mesh Management.

  3. On the Mesh Management page, click the name of the ASM instance.

  4. In the left-side navigation pane, choose Cluster & Workload Management > External Service(ServiceEntry).

  5. Click Create from YAML.

  6. Select the namespace where the sleep application resides, select Access mesh external services for Template, paste the following YAML, and click Create:

       apiVersion: networking.istio.io/v1beta1
       kind: ServiceEntry
       metadata:
         name: external-svc-http
       spec:
         hosts:
         - aliyun.com
         location: MESH_EXTERNAL
         ports:
         - number: 80
           name: http
           protocol: HTTP
         resolution: DNS
  7. Verify that the pod can reach the external service: A 301 confirms that aliyun.com is registered in the service registry. At this point, the pod still sends requests directly to the external service -- traffic does not yet pass through the egress gateway.

       kubectl exec -it <sleep-pod-name> -- curl -I aliyun.com

Step 4: Route traffic through the egress gateway

With the ServiceEntry in place, configure an Istio Gateway and a VirtualService to route all traffic to aliyun.com through the egress gateway.

4a. Create the egress gateway

Create an egress gateway that accepts HTTP traffic on port 80.

For detailed instructions, see Create an egress gateway.

4b. Create an Istio Gateway

Create an Istio Gateway that binds to the egress gateway and listens on port 80 for traffic to aliyun.com.

For detailed instructions, see Manage Istio gateways.

Create gateway rule

4c. Create a VirtualService

The VirtualService defines two route rules:

  • mesh to egress gateway -- Sidecars redirect outbound traffic to the egress gateway service.

  • egress gateway to external host -- The egress gateway forwards traffic to the actual external service.

Create a VirtualService with the following YAML. For detailed instructions, see Manage virtual services.

Expand to view VirtualService YAML

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: egressgateway-vs
spec:
  hosts:
  - aliyun.com
  gateways:
  - egress-gw        # Name of the Istio Gateway created in step 4b
  - mesh
  http:
  - match:
    - gateways:
      - mesh          # Match traffic from sidecars
      port: 80
    route:
    - destination:
        host: istio-egressgateway.istio-system.svc.cluster.local
        port:
          number: 80
      weight: 100
  - match:
    - gateways:
      - egress-gw     # Match traffic arriving at the egress gateway
      port: 80
    route:
    - destination:
        host: aliyun.com
        port:
          number: 80
      weight: 100

4d. Verify the configuration

  1. Send a request from the sleep pod: A 301 confirms the external service is reachable. Traffic now flows through the egress gateway instead of directly from the pod.

       kubectl exec -it <sleep-pod-name> -- curl -I aliyun.com
  2. Check the egress gateway access logs to confirm the request passed through it: Replace <egress-gateway-pod-name> with the name of the egress gateway pod in the istio-system namespace. Expected output (JSON format): The downstream_remote_address field shows the sleep pod's IP address, confirming the request originated from the application and was routed through the egress gateway.

    Note
    • If the egress gateway has multiple replicas, check logs on each replica to find the access record.

    • If you have enabled access logging, you can also view records in the Simple Log Service console.

       kubectl -n istio-system logs <egress-gateway-pod-name> -c istio-proxy | grep aliyun.com | tail -n 1
       {
         "downstream_remote_address": "10.34.0.140:47942",
         "upstream_host": "106.11.XXX.XX:80",
         "upstream_cluster": "outbound|80||aliyun.com",
         "response_code": 301,
         "method": "HEAD",
         "authority": "aliyun.com",
         ...
       }

Cleanup

To remove the resources created in this tutorial, run:

kubectl delete -f sleep.yaml

Also delete the following resources from the ASM console:

  • ServiceEntry: external-svc-http

  • VirtualService: egressgateway-vs

  • The Istio Gateway created in step 4b

  • The egress gateway created in step 4a

If you changed the outbound traffic policy to REGISTRY_ONLY, revert it to ALLOW_ANY if your environment requires open external access.

See also