All Products
Search
Document Center

Alibaba Cloud Service Mesh:Use an ASM egress gateway to access external mTLS services

Last Updated:Mar 11, 2026

When your applications send plaintext HTTP to external services, traffic leaving the mesh is unencrypted. The Service Mesh (ASM) egress gateway acts as a single exit point for all outbound mesh traffic. It intercepts outgoing requests and initiates mTLS connections to external services on behalf of your workloads, providing end-to-end encryption without code changes.

This guide walks you through routing outbound HTTP traffic through an egress gateway and upgrading it to mTLS.

Use cases

Route traffic through an egress gateway with mTLS origination when:

  • Your security policy requires encrypted egress. Applications send plaintext HTTP, but all traffic leaving the mesh must be encrypted. The egress gateway handles TLS origination with no application changes.

  • You need centralized egress control. All outbound traffic must flow through dedicated gateway nodes for auditing, monitoring, and policy enforcement.

  • The external service requires client certificate authentication. The mesh must present a specific client certificate (mutual TLS) on behalf of your workloads.

How it works

The following diagram shows the complete traffic path after you finish all configuration steps in this guide:

sleep pod ──mesh mTLS──> egress gateway ──external mTLS──> external ingress gateway ──mTLS──> httpbin
  1. The sleep pod sends a plaintext HTTP request to test.com.

  2. The sidecar proxy intercepts the request and forwards it to the egress gateway over mesh-internal mTLS (certificates managed by ASM).

  3. The egress gateway initiates a new mTLS connection to the external service using a user-provided client certificate.

  4. The external ingress gateway terminates mTLS and routes the request to the upstream httpbin service.

The guide builds this path incrementally. After Step 3, the egress-to-external segment is still plaintext. Step 4 upgrades that segment to mTLS, closing the security gap.

Prerequisites

Before you begin, make sure that you have:

Note

In the steps below, $INGRESS_GATEWAY_IP refers to the ingress gateway IP from the prerequisite tutorial. "ACK cluster" and "ASM instance" refer to the primary environment resources.

Set the shell variable used throughout this guide. With the kubeconfig of the ACK cluster, run:

# Replace with the actual ingress gateway IP from the prerequisite tutorial
export INGRESS_GATEWAY_IP=<ingress-gateway-ip>

Step 1: Deploy the sleep test application

Deploy the sleep application as a test client. For deployment instructions, see Related operations.

Verify the deployment

With the kubeconfig of the ACK cluster, verify that the sleep pod can reach the external HTTPBin service:

kubectl exec deploy/sleep -- curl --header "host:test.com" $INGRESS_GATEWAY_IP/status/418

Expected output:

    -=[ teapot ]=-

       _...._
     .'  _ _ `.
    | ."` ^ `". _,
    \_;`"---"`|//
      |       ;/
      \_     _/
        `"""`

The HTTP 418 teapot response confirms connectivity to the external HTTPBin service.

Step 2: Enable REGISTRY_ONLY and create a ServiceEntry

Restrict outbound access (optional)

Set the outbound traffic policy to REGISTRY_ONLY to block pods from reaching any service not explicitly registered through a ServiceEntry. For more information, see Step 2: Enable REGISTRY_ONLY.

After you enable REGISTRY_ONLY, requests to unregistered services return a 502 Bad Gateway error.

Register the external service

Create a ServiceEntry for test.com so that pods in the mesh can reach it. For more information, see Create an external service.

apiVersion: networking.istio.io/v1beta1
kind: ServiceEntry
metadata:
  name: test-com
  namespace: default
spec:
  endpoints:
    - address: <ingress-gateway-ip>  # IP address of the external ingress gateway
  hosts:
    - test.com
  location: MESH_EXTERNAL
  ports:
    - name: http
      number: 80
      protocol: HTTP
    - name: https
      number: 443
      protocol: HTTPS
  resolution: STATIC

Replace <ingress-gateway-ip> with the IP address of the ingress gateway from the prerequisite tutorial.

Verify

After you apply the ServiceEntry, run the same curl command from Step 1 to confirm that the sleep pod can still reach the HTTPBin service at test.com.

Step 3: Create the egress gateway and route HTTP traffic through it

3a. Create the egress gateway

Create an egress gateway for HTTP traffic on port 80 with mutual TLS authentication enabled. Workloads in the mesh automatically encrypt traffic to the gateway using ASM-managed certificates. For more information, see Create an egress gateway.

3b. Create gateway rules

Create a Gateway resource that declares the egress gateway listener. The gateway listens on port 80 with ISTIO_MUTUAL TLS mode (ASM-managed certificates). For more information, see Create gateway rules.

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: egress-gateway
  namespace: default
spec:
  selector:
    istio: egressgateway
  servers:
  - hosts:
    - '*'
    port:
      name: http
      number: 80
      protocol: HTTPS
    tls:
      mode: ISTIO_MUTUAL

3c. Create a VirtualService

Create a VirtualService that routes test.com traffic in two stages:

  1. Mesh to egress gateway: The sidecar forwards traffic on port 80 to the egress gateway.

  2. Egress gateway to external service: The egress gateway forwards traffic to the test.com ServiceEntry on port 80.

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: egressgateway-vs
spec:
  hosts:
  - test.com
  gateways:
  - egress-gateway  # Gateway created in step 3b
  - mesh
  http:
  - match:
    - gateways:
      - mesh
      port: 80
    route:
    - destination:
        host: istio-egressgateway.istio-system.svc.cluster.local
        port:
          number: 80
      weight: 100
  - match:
    - gateways:
      - egress-gateway
      port: 80
    route:
    - destination:
        host: test.com
        port:
          number: 80
      weight: 100

Verify that traffic flows through the egress gateway

  1. Access test.com from the sleep pod: Expected output: the HTTP 418 teapot ASCII art (same as Step 1).

       kubectl exec deploy/sleep -- curl --header "host:test.com" $INGRESS_GATEWAY_IP/status/418
  2. Check the egress gateway access log. With the kubeconfig of the ASM instance, run: The log entry should show "upstream_cluster":"outbound|80||test.com", which confirms that the request passed through the egress gateway.

       export EGRESS_POD=$(kubectl get pod -l istio=egressgateway -n istio-system -o jsonpath='{.items[0].metadata.name}')
       kubectl -n istio-system logs $EGRESS_POD | tail -1

Traffic flow at this stage

sleep pod ──mTLS──> egress gateway ──plaintext──> external ingress gateway ──mTLS──> httpbin

The egress-to-external segment is still plaintext. Step 4 upgrades this segment to mTLS.

Step 4: Upgrade egress traffic to mTLS

4a. Update the VirtualService

Update the VirtualService to route egress gateway traffic to port 443 instead of port 80. The only change is the destination port for the egress-gateway match:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: egressgateway-vs
spec:
  hosts:
  - test.com
  gateways:
  - egress-gateway
  - mesh
  http:
  - match:
    - gateways:
      - mesh
      port: 80
    route:
    - destination:
        host: istio-egressgateway.istio-system.svc.cluster.local
        port:
          number: 80
      weight: 100
  - match:
    - gateways:
      - egress-gateway
      port: 80
    route:
    - destination:
        host: test.com
        port:
          number: 443  # Changed from 80 to 443
      weight: 100

4b. Import the mTLS client certificate

Import the mTLS certificate from Configure mTLS service on ASM ingress gateway and restrict specific client access. Name the certificate test.client. For more information, see Use the certificate management feature of ASM.

Alternatively, create the secret directly with kubectl. With the kubeconfig of the ACK cluster, run:

kubectl create -n istio-system secret generic test.client \
  --from-file=tls.key=client.key.pem \
  --from-file=tls.crt=clientcert.pem \
  --from-file=ca.crt=cacert.pem

4c. Create a DestinationRule for mTLS origination

Create a DestinationRule that configures the egress gateway to initiate mTLS when connecting to test.com on port 443:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: originate-mtls-for-test-com
spec:
  host: test.com
  trafficPolicy:
    loadBalancer:
      simple: ROUND_ROBIN
    portLevelSettings:
    - port:
        number: 443
      tls:
        mode: MUTUAL
        credentialName: test.client
        sni: test.com
FieldDescription
mode: MUTUALEnables mutual TLS. The egress gateway presents a client certificate and validates the server certificate.
credentialNameReferences the test.client secret containing the client certificate, key, and CA certificate.
sniSets the Server Name Indication (SNI) extension to test.com for the TLS handshake.

Verify mTLS origination

  1. Test a path that the client certificate is allowed to access: Expected output:

       kubectl exec deploy/sleep -it -- curl --header "host:test.com" $INGRESS_GATEWAY_IP/status/200 -I
       HTTP/1.1 200 OK
       server: envoy
       date: Mon, 29 Jul 2024 03:33:50 GMT
       content-type: text/html; charset=utf-8
       access-control-allow-origin: *
       access-control-allow-credentials: true
       content-length: 0
       x-envoy-upstream-service-time: 5
  2. Test a path that the client certificate is denied from accessing: Expected output: The request is denied because the test.client certificate is configured in the prerequisite tutorial to block access to the /status/418 path.

       kubectl exec deploy/sleep -it -- curl --header "host:test.com" $INGRESS_GATEWAY_IP/status/418
       RBAC: access denied%
  3. Confirm the egress gateway connects over port 443. With the kubeconfig of the ASM instance, run: The log entry should show "upstream_cluster":"outbound|443||test.com" and "upstream_host":"...:443", which confirms the egress gateway connects to the external service over the mTLS port.

       kubectl -n istio-system logs $EGRESS_POD | tail -1

Traffic flow after mTLS upgrade

sleep pod ──mTLS──> egress gateway ──mTLS──> external ingress gateway ──mTLS──> httpbin

All segments are now encrypted with mTLS. The plaintext gap from Step 3 is eliminated.

Step 5: Configure authorization policies

With end-to-end mTLS in place, the mesh authenticates client identities at every hop. Authorization policies enforce fine-grained access control at two points.

Restrict access at the egress gateway

Apply an AuthorizationPolicy on the egress gateway to control which in-mesh services can reach test.com. The following example denies the sleep service account access to the /headers path:

apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  labels:
    gateway: egressgateway
  name: test
  namespace: istio-system
spec:
  action: DENY
  rules:
    - from:
        - source:
            principals:
              - cluster.local/ns/default/sa/sleep
      to:
        - operation:
            hosts:
              - test.com
            paths:
              - /headers
  selector:
    matchLabels:
      istio: egressgateway

After you apply this policy, requests from the sleep pod to test.com/headers through the egress gateway are denied.

Restrict access at the external ingress gateway

The AuthorizationPolicy on the external ingress gateway (from Configure mTLS service on ASM ingress gateway and restrict specific client access) restricts access based on client certificate identity. The prerequisite tutorial policy prevents the test.client certificate from accessing /status/418.

Together, these two authorization points provide layered access control:

Authorization pointScope
Egress gateway policiesControl which in-mesh services can reach which external hosts and paths.
External ingress gateway policiesControl which client certificates can access which backend paths.

What's next