In a production environment, a compromised or misconfigured service can reach databases it should never access. For example, a service in a development namespace could connect to a production database, exposing sensitive data.
Service Mesh (ASM) solves this with its zero-trust security model. By routing egress traffic through a dedicated egress gateway and applying an AuthorizationPolicy, you enforce namespace-level access control for outbound database connections. Only services in approved namespaces can reach the external database -- all others are denied.
This topic walks through a complete example: routing traffic from the demo-server namespace through an egress gateway to an external ApsaraDB RDS for MySQL database, then using an authorization policy to allow or deny that traffic.
How it works
The solution uses three layers of control:
Outbound traffic policy -- Set to
REGISTRY_ONLYso that only explicitly registered external services (ServiceEntries) are reachable from the mesh.Egress gateway routing -- A Gateway, DestinationRule, and VirtualService route database traffic from the sidecar proxy through the egress gateway to the external database. Mutual TLS (mTLS) secures the connection between the sidecar and the gateway.
Authorization policy -- An AuthorizationPolicy on the egress gateway controls which namespaces are allowed or denied access.
+-----------------+ mTLS +-----------------+ +--------------+
| demo-server | ----------------> | Egress gateway | -----------> | External |
| (sidecar) | port 13306 | (authz policy) | port 3306 | RDS MySQL |
+-----------------+ +-----------------+ +--------------+Prerequisites
Before you begin, make sure that you have:
A cluster added to your ASM instance. For more information, see Add a cluster to an ASM instance
An ApsaraDB RDS for MySQL instance with a known connection endpoint and port
kubectlconfigured to connect to your cluster. For more information, see Obtain the kubeconfig file of a cluster and use kubectl to connect to the cluster
Step 1: Enable automatic sidecar injection
Create a namespace named demo-server and enable automatic sidecar proxy injection for it. Once enabled, every pod deployed in this namespace receives a sidecar proxy that intercepts all inbound and outbound traffic, bringing it under mesh control.
For detailed instructions, see Manage global namespaces.
Step 2: Deploy a MySQL client
Deploy a MySQL client in the demo-server namespace. This workload serves as the test client for verifying authorization policy behavior later.
Encode the database connection password in Base64:
echo <database-connection-password> | base64Create a file named
k8s-mysql.yamlwith the following content: Replace{yourPasswordBase64}with the Base64-encoded value from the previous step.apiVersion: v1 data: password: {yourPasswordBase64} # The Base64-encoded database password. kind: Secret metadata: name: mysql-pass type: Opaque --- apiVersion: apps/v1 kind: Deployment metadata: labels: name: lbl-k8s-mysql name: k8s-mysql spec: progressDeadlineSeconds: 600 replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: name: lbl-k8s-mysql strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate template: metadata: labels: name: lbl-k8s-mysql spec: containers: - env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: key: password name: mysql-pass image: 'mysql:latest' imagePullPolicy: Always name: mysql ports: - containerPort: 3306 name: mysql protocol: TCP resources: limits: cpu: 500m terminationMessagePath: /dev/termination-log terminationMessagePolicy: File volumeMounts: - mountPath: /var/lib/mysql name: k8s-mysql-storage dnsPolicy: ClusterFirst restartPolicy: Always schedulerName: default-scheduler securityContext: {} terminationGracePeriodSeconds: 30 volumes: - emptyDir: {} name: k8s-mysql-storageDeploy the Secret and Deployment to the
demo-servernamespace:kubectl apply -f k8s-mysql.yaml -n demo-serverConfirm that a sidecar proxy was injected into the MySQL client pod: On the Container tab, a container named
istio-proxyconfirms that sidecar proxy injection succeeded.Log on to the ACK console. In the left-side navigation pane, click Clusters.
On the Clusters page, click the name of your cluster and choose Workloads > Pods in the left-side navigation pane.
On the Pods page, click the pod name of the MySQL client.
Step 3: Create an egress gateway
An egress gateway centralizes outbound traffic from the mesh, giving you a single enforcement point for authorization policies. Without it, traffic leaves the mesh directly from each sidecar, making namespace-level access control impractical.
Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.
On the Mesh Management page, click the name of your ASM instance. In the left-side navigation pane, choose ASM Gateways > Egress Gateway.
On the Egress Gateway page, click Create.
Configure the following parameters, then click Create:
Parameter Value Name egressgatewayCluster Select your cluster Protocol (Port Mapping) TCP Service Port (Port Mapping) 13306
For a full description of each parameter, see Create an egress gateway.
Step 4: Restrict outbound traffic and register the external database
By default, services in an ASM instance can access all external services. To enforce access control, restrict outbound traffic to registered services only, then register the external database as a ServiceEntry.
Set the outbound traffic policy
Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.
On the Mesh Management page, click the name of your ASM instance. In the left-side navigation pane, choose Dataplane Component Management > Sidecar Proxy Setting.
On the global tab, click Outbound Traffic Policy, set it to REGISTRY_ONLY, and click Update Settings.
After this change, only external services registered as ServiceEntries are reachable from the mesh.
Register the external database as a ServiceEntry
On the details page of your ASM instance, choose Cluster & Workload Management > External Service(ServiceEntry) in the left-side navigation pane, then click Create from YAML.
Select istio-system from the Namespace drop-down list and paste the following YAML:
apiVersion: networking.istio.io/v1beta1 kind: ServiceEntry metadata: name: demo-server-rds namespace: demo-server spec: endpoints: - address: rm-xxxxxxx.mysql.xxxx.rds.aliyuncs.com # Replace with your RDS endpoint. ports: tcp: 3306 hosts: - rm-xxxxxxx.mysql.xxxx.rds.aliyuncs.com location: MESH_EXTERNAL ports: - name: tcp number: 3306 # The port of the external database. protocol: TCP # The protocol used by the external database. resolution: DNSClick Create.
Step 5: Route traffic through the egress gateway
Create three Istio resources -- a Gateway, a DestinationRule, and a VirtualService -- that define the TCP routing path: sidecar (port 3306) --> egress gateway (port 13306) --> external database (port 3306). mTLS secures traffic between the sidecar and the egress gateway.
Create the Gateway
The Gateway resource configures the egress gateway to accept inbound mTLS connections on port 13306. Services must present a valid Istio-issued certificate before traffic is forwarded.
On the details page of your ASM instance, choose ASM Gateways > Gateway in the left-side navigation pane, then click Create from YAML.
Select istio-system from the Namespace drop-down list and paste the following YAML: The
ISTIO_MUTUALTLS mode enables mTLS authentication between the sidecar and the egress gateway.apiVersion: networking.istio.io/v1beta1 kind: Gateway metadata: name: istio-egressgateway namespace: istio-system spec: selector: istio: egressgateway servers: - hosts: - '*' port: name: http-0 number: 13306 protocol: TLS tls: mode: ISTIO_MUTUALClick Create.
Create the DestinationRule
The DestinationRule configures the sidecar to use mTLS when connecting to the egress gateway, and sets the Server Name Indication (SNI) to the external database host for correct TLS routing.
On the details page of your ASM instance, choose Traffic Management Center > DestinationRule in the left-side navigation pane, then click Create from YAML.
Select demo-server from the Namespace drop-down list and paste the following YAML: The
snifield must match the host of the external database so that TLS routing works correctly.apiVersion: networking.istio.io/v1beta1 kind: DestinationRule metadata: name: demo-server-egress-gateway namespace: demo-server spec: host: istio-egressgateway.istio-system.svc.cluster.local subsets: - name: mysql-gateway-mTLS trafficPolicy: loadBalancer: simple: ROUND_ROBIN portLevelSettings: - port: number: 13306 # The egress gateway port. tls: mode: ISTIO_MUTUAL sni: rm-xxxxxxx.mysql.xxxx.rds.aliyuncs.com # Replace with your RDS endpoint.Click Create.
Create the VirtualService
The VirtualService defines two TCP routing rules that chain the traffic path from sidecar to egress gateway to external database.
On the details page of your ASM instance, choose Traffic Management Center > VirtualService in the left-side navigation pane, then click Create from YAML.
Select demo-server from the Namespace drop-down list and paste the following YAML: This VirtualService defines two TCP routing rules:
Rule Gateway match Behavior Rule 1 meshCaptures outbound traffic from sidecars on port 3306 and redirects it to port 13306 on the egress gateway Rule 2 istio-system/istio-egressgatewayCaptures traffic arriving at the egress gateway on port 13306 and forwards it to port 3306 on the external database apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: demo-server-through-egress-gateway namespace: demo-server spec: exportTo: - istio-system - demo-server gateways: - mesh - istio-system/istio-egressgateway hosts: - rm-xxxxxxx.mysql.xxxx.rds.aliyuncs.com tcp: - match: - gateways: - mesh port: 3306 route: - destination: host: istio-egressgateway.istio-system.svc.cluster.local port: number: 13306 subset: mysql-gateway-mTLS weight: 100 - match: - gateways: - istio-system/istio-egressgateway port: 13306 route: - destination: host: rm-xxxxxxx.mysql.xxxx.rds.aliyuncs.com port: number: 3306 weight: 100Click Create.
Step 6: Test the authorization policy
With all routing in place, create an authorization policy on the egress gateway to verify namespace-level access control. First deny traffic, then allow it.
Deny access
Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.
On the Mesh Management page, click the name of your ASM instance. In the left-side navigation pane, choose Mesh Security Center > AuthorizationPolicy, then click Create.
Configure the following parameters, then click Create:
Parameter Value Name Enter a name for the policy Policy Type DENY ASM Gateway (Gateway Scope tab) egressgateway(the Match Label auto-fills toistio:egressgateway)Namespaces (Request Matching Rules) Turn on Namespaces and set it to demo-frontend
Verify that access is denied: Expected result: The
ERROR 2013error is returned, confirming that the DENY policy blocks traffic from thedemo-servernamespace to the external database.Log on to the ACK console. In the left-side navigation pane, click Clusters.
On the Clusters page, click the name of your cluster and choose Workloads > Pods in the left-side navigation pane.
On the Pods page, find the
k8s-mysqlpod and click Terminal in the Actions column. Then click Container: MySQL.Run the following command to connect to the external database:
mysql --user=root --password=$MYSQL_ROOT_PASSWORD --host rm-xxxxxxx.mysql.xxxx.rds.aliyuncs.com
Allow access
Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.
On the Mesh Management page, click the name of your ASM instance. In the left-side navigation pane, choose Mesh Security Center > AuthorizationPolicy.
Find the authorization policy and click YAML in the Actions column.
In the Edit dialog box, change the value of the
actionparameter toALLOW, then click OK.Run the same command on the terminal of the
k8s-mysqlcontainer: Expected result: TheWelcome to the MySQL monitormessage is returned, confirming that the ALLOW policy permits traffic from thedemo-servernamespace to the external database.mysql --user=root --password=$MYSQL_ROOT_PASSWORD --host rm-xxxxxxx.mysql.xxxx.rds.aliyuncs.com
These results confirm that an authorization policy on the egress gateway effectively controls which namespaces can reach an external database.
Cleanup
To remove the resources created in this topic, run the following commands:
# Delete the MySQL client
kubectl delete -f k8s-mysql.yaml -n demo-server
# Delete Istio resources (run from the ASM console or with kubectl against the ASM API server)
kubectl delete virtualservice demo-server-through-egress-gateway -n demo-server
kubectl delete destinationrule demo-server-egress-gateway -n demo-server
kubectl delete gateway istio-egressgateway -n istio-system
kubectl delete serviceentry demo-server-rds -n demo-serverAlso remove the egress gateway and authorization policy from the ASM console.