All Products
Search
Document Center

Alibaba Cloud Service Mesh:Configure mTLS on the ASM ingress gateway and restrict client access

Last Updated:Mar 11, 2026

Mutual TLS (mTLS) on the ASM ingress gateway requires clients to present a certificate during the TLS handshake, proving their identity. After authentication, authorization policies control what each client identity can access. This two-layer approach separates authentication (mTLS verifies who the client is) from authorization (policies control what the client can do).

This guide walks through three tasks:

  1. Generate the certificates for mTLS (CA, server, and client).

  2. Configure the ASM ingress gateway to require client certificates on port 443.

  3. Create an authorization policy that denies a specific client identity access to a specific path.

How client identity maps to authorization rules

The client certificate includes a SPIFFE URI in its Subject Alternative Name (SAN), such as spiffe://test.client. ASM authorization policies use the principals field to match this identity. The policy matches the value after the spiffe:// prefix. For example, a principals value of test.client matches a certificate with URI.1 = spiffe://test.client.

This mapping is the core mechanism that connects mTLS authentication to path-based access control.

Prerequisites

Before you begin, make sure that you have:

Step 1: Generate mTLS certificates

mTLS requires three certificate sets: a root CA, a server certificate, and a client certificate. The root CA signs both the server and client certificates. The gateway presents the server certificate to identify itself, and clients present the client certificate to prove their identity.

Note

When prompted for certificate fields during generation, accept the default values. The configuration files below pre-set all required fields.

  1. Create a file named ca.cnf with the following content.

    ca.cnf

    HOME = .
    RANDFILE = $ENV::HOME/.rnd
    ####################################################################
    [ ca ]
    default_ca = CA_default # The default ca section
    [ CA_default ]
    default_days = 1000 # how long to certify for
    default_crl_days = 30 # how long before next CRL
    default_md = sha256 # use public key default MD
    preserve = no # keep passed DN ordering
    x509_extensions = ca_extensions # The extensions to add to the cert
    email_in_dn = no # Don't concat the email in the DN
    copy_extensions = copy # Required to copy SANs from CSR to cert
    
    #====Following 7 lines are for signing other certificates, not for making the CA certificate.====
    base_dir = .
    certificate = $base_dir/cacert.pem # The CA certifcate
    private_key = $base_dir/cakey.pem # The CA private key
    new_certs_dir = $base_dir # Location for new certs after signing
    database = $base_dir/index.txt # Database index file
    serial = $base_dir/serial.txt # The current serial number
    unique_subject = no # Set to 'no' to allow creation of several certificates with same subject.
    
    ####################################################################
    [ req ]
    default_bits = 4096
    default_keyfile = cakey.pem
    distinguished_name = ca_distinguished_name
    x509_extensions = ca_extensions
    string_mask = utf8only
    ####################################################################
    [ ca_distinguished_name ]
    countryName = Country Name (2 letter code)
    countryName_default = CN
    stateOrProvinceName = State or Province Name (full name)
    stateOrProvinceName_default = bj
    localityName = Locality Name (eg, city)
    localityName_default = bj
    organizationName = Organization Name (eg, company)
    organizationName_default = test-asm
    organizationalUnitName = Organizational Unit (eg, division)
    organizationalUnitName_default = R&D
    commonName = Common Name (e.g. server FQDN or YOUR name)
    commonName_default = Test CA
    emailAddress = Email Address
    emailAddress_default = test@example.com
    ####################################################################
    [ ca_extensions ]
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid:always, issuer
    basicConstraints = critical, CA:true
    keyUsage = keyCertSign, cRLSign
    
    
    #====All lines below are for signing other certs, not for making the CA cert.======
    
    ####################################################################
    [ signing_policy ]
    countryName = optional
    stateOrProvinceName = optional
    localityName = optional
    organizationName = optional
    organizationalUnitName = optional
    commonName = supplied
    emailAddress = optional
    ####################################################################
    [ signing_req ]
    subjectKeyIdentifier = hash
    authorityKeyIdentifier = keyid,issuer
    basicConstraints = CA:FALSE
    keyUsage = digitalSignature, keyEncipherment
  2. Generate the root CA certificate and private key.

    openssl req -x509 -config ca.cnf -newkey rsa:4096 -sha256 -nodes -out cacert.pem -outform PEM

    This produces two files: cacert.pem (the CA certificate) and cakey.pem (the CA private key).

  3. Create a file named server.cnf with the following content.

    HOME = .
    RANDFILE = $ENV::HOME/.rnd
    ####################################################################
    [ req ]
    default_bits = 2048
    default_keyfile = serverkey.pem
    distinguished_name = server_distinguished_name
    req_extensions = server_req_extensions
    string_mask = utf8only
    ####################################################################
    [ server_distinguished_name ]
    countryName = Country Name (2 letter code)
    countryName_default = CN
    stateOrProvinceName = State or Province Name (full name)
    stateOrProvinceName_default = bj
    localityName = Locality Name (eg, city)
    localityName_default = bj
    organizationName = Organization Name (eg, company)
    organizationName_default = test
    commonName = Common Name (e.g. server FQDN or YOUR name)
    commonName_default = test.com
    emailAddress = Email Address
    emailAddress_default = test@example.com
    ####################################################################
    [ server_req_extensions ]
    subjectKeyIdentifier = hash
    basicConstraints = CA:FALSE
    keyUsage = digitalSignature, keyEncipherment
    subjectAltName = @alternate_names
    nsComment = "OpenSSL Generated Certificate"
    ####################################################################
    [ alternate_names ]
    DNS.1 = test.com
  4. Generate the server certificate, initialize the CA signing database, and sign the certificate.

    # Generate the CSR and private key
    openssl req -config server.cnf -newkey rsa:2048 -sha256 -nodes -out server.csr -outform PEM
    
    # Initialize the CA signing database
    touch index.txt
    echo '01' > serial.txt
    
    # Sign the server certificate with the root CA
    openssl ca -config ca.cnf -policy signing_policy -extensions signing_req -out servercert.pem -infiles server.csr

    This produces two files: servercert.pem (the server certificate) and serverkey.pem (the server private key).

  5. Create a file named client.cnf with the following content.

    HOME = .
    RANDFILE = $ENV::HOME/.rnd
    ####################################################################
    [ req ]
    default_bits = 2048
    default_keyfile = client.key.pem
    distinguished_name = server_distinguished_name
    req_extensions = server_req_extensions
    string_mask = utf8only
    ####################################################################
    [ server_distinguished_name ]
    countryName = Country Name (2 letter code)
    countryName_default = CN
    stateOrProvinceName = State or Province Name (full name)
    stateOrProvinceName_default = bj
    localityName = Locality Name (eg, city)
    localityName_default = bj
    organizationName = Organization Name (eg, company)
    organizationName_default = test.client
    commonName = Common Name (e.g. server FQDN or YOUR name)
    commonName_default = test.client
    emailAddress = Email Address
    emailAddress_default = test.client@example.com
    ####################################################################
    [ server_req_extensions ]
    subjectKeyIdentifier = hash
    basicConstraints = CA:FALSE
    keyUsage = digitalSignature, keyEncipherment
    subjectAltName = @alternate_names
    nsComment = "OpenSSL Generated Certificate"
    ####################################################################
    [ alternate_names ]
    URI.1 = spiffe://test.client

    Two fields in this configuration determine the client identity for authorization:

    Field Value Role
    commonName_default test.client Identifies the client
    URI.1 (SAN) spiffe://test.client Matched by the principals field in authorization policies (after the spiffe:// prefix)
  6. Generate the client certificate and sign it with the root CA.

    # Generate the CSR and private key
    openssl req -config client.cnf -newkey rsa:2048 -sha256 -nodes -out clientcert.csr -outform PEM
    
    # Sign the client certificate with the root CA
    openssl ca -config ca.cnf -policy signing_policy -extensions signing_req -out clientcert.pem -infiles clientcert.csr

    This produces two files: clientcert.pem (the client certificate) and client.key.pem (the client private key).

  7. Import the mTLS certificate using the ASM certificate management feature. Set the certificate name to test.com. For more information, see Use the certificate management feature of ASM.

    Alternatively, create a Kubernetes Secret directly. Run the following command using the kubeconfig of your data plane cluster:

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

    This Secret bundles three files:

    Key File Purpose
    tls.key serverkey.pem Server private key for TLS termination
    tls.crt servercert.pem Server certificate presented to clients
    ca.crt cacert.pem Root CA certificate used to verify client certificates

Step 2: Configure the mTLS listener on the gateway

Add an mTLS listener on port 443 of the ASM ingress gateway. After this configuration, external clients must present a valid client certificate to access the httpbin service.

  1. Update the Gateway resource with the following content.

    apiVersion: networking.istio.io/v1beta1
    kind: Gateway
    metadata:
      name: httpbin
      namespace: default
    spec:
      selector:
        istio: ingressgateway
      servers:
        - hosts:
            - '*'
          port:
            name: test
            number: 80
            protocol: HTTP
        - hosts:
          - test.com
          port:
            number: 443
            name: https
            protocol: HTTPS
          tls:
            mode: MUTUAL
            credentialName: test.com

    The tls.mode: MUTUAL setting requires clients to present a certificate signed by the CA in the test.com Secret. Without a valid client certificate, the TLS handshake fails and the gateway rejects the connection.

    Note

    Other TLS modes include SIMPLE (server-side TLS only, no client certificate required) and PASSTHROUGH (TLS is not terminated at the gateway). Use MUTUAL when you need to authenticate client identity at the gateway level.

  2. Verify that mTLS rejects requests without a client certificate.

    curl -v --resolve "test.com:443:<ASM-gateway-IP>" \
      --cacert cacert.pem \
      https://test.com/status/200

    Replace <ASM-gateway-IP> with the IP address of your ASM ingress gateway.

    The request fails with a TLS handshake error because no client certificate is provided. This confirms that the gateway enforces mTLS.

  3. Verify that mTLS accepts requests with a valid client certificate.

    curl --header "host:test.com" \
         --resolve "test.com:443:<ASM-gateway-IP>" \
         --cacert cacert.pem \
         --cert clientcert.pem \
         --key client.key.pem \
         https://test.com/status/200 -I

    Expected output:

    HTTP/2 200
    server: istio-envoy
    date: Sun, 28 Jul 2024 7:30:30 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: 6

    An HTTP/2 200 response confirms that the gateway accepted the client certificate and forwarded the request to httpbin.

Step 3: Restrict client access with an authorization policy

After mTLS authenticates each client, authorization policies control what each client can access. In this step, create a DENY policy that blocks the test.client identity from accessing a specific path. Other clients remain unaffected.

Note

When multiple authorization policies exist on the same workload, DENY policies are evaluated before ALLOW policies. A request that matches any DENY rule is rejected regardless of ALLOW rules.

  1. Deploy the following AuthorizationPolicy to deny test.client access to the /status/418 path on the httpbin application. For more information, see Configure authorization policies for HTTP requests.

    apiVersion: security.istio.io/v1beta1
    kind: AuthorizationPolicy
    metadata:
      name: test
      namespace: istio-system
    spec:
      action: DENY
      rules:
        - from:
            - source:
                principals:
                  - test.client
          to:
            - operation:
                paths:
                  - /status/418
      selector:
        matchLabels:
          istio: ingressgateway

    This policy targets the ingress gateway (istio: ingressgateway) and denies any request from the test.client principal to the /status/418 path. The principals value test.client matches the SPIFFE URI spiffe://test.client in the client certificate's SAN.

Verify the authorization policy

  1. Confirm that test.client can still access other paths. Send a request to /status/200:

    curl --header "host:test.com" \
         --resolve "test.com:443:<ASM-gateway-IP>" \
         --cacert cacert.pem \
         --cert clientcert.pem \
         --key client.key.pem \
         https://test.com/status/200 -I

    Expected output:

    HTTP/2 200
    server: istio-envoy
    date: Sun, 28 Jul 2024 7:33:30 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: 6

    A 200 response means the DENY policy applies only to /status/418, not other paths.

  2. Confirm that test.client is denied access to /status/418:

    curl --header "host:test.com" \
         --resolve "test.com:443:<ASM-gateway-IP>" \
         --cacert cacert.pem \
         --cert clientcert.pem \
         --key client.key.pem \
         https://test.com/status/418

    Expected output:

    RBAC: access denied

    RBAC: access denied confirms the authorization policy blocked test.client from this path.

  3. Confirm that a different client identity can still access /status/418. Send a request using the server certificate instead:

    curl --header "host:test.com" \
         --resolve "test.com:443:<ASM-gateway-IP>" \
         --cacert cacert.pem \
         --cert servercert.pem \
         --key serverkey.pem \
         https://test.com/status/418

    Expected output:

        -=[ teapot ]=-
    
           _...._
         .'  _ _ `.
        | ."` ^ `". _,
        \_;`"---"`|//
          |       ;/
          \_     _/
            `"""`

    The teapot response confirms the DENY rule targets only the test.client identity. Other identities with valid certificates can still access /status/418.