All Products
Search
Document Center

Alibaba Cloud Service Mesh:Configure mTLS for a gRPC service on an ingress gateway

Last Updated:Mar 11, 2026

Mutual TLS (mTLS) on an ingress gateway enforces two-way certificate verification: only clients that present a valid certificate can reach your backend services. This prevents unauthorized access, eavesdropping, and data tampering for gRPC traffic without requiring TLS configuration on individual applications.

How mTLS works with gRPC on an ingress gateway

gRPC runs on HTTP/2. Ingress gateways support gRPC with TLS or mTLS. When a request reaches the ingress gateway, TLS termination happens at the gateway: it decrypts the incoming TCP stream, validates the client certificate against a trusted CA, and forwards plaintext traffic to the backend service inside the mesh. Individual applications do not need their own TLS configuration.

The following diagram shows the traffic flow:

Client (with cert) --[mTLS]--> Ingress Gateway --[plaintext]--> gRPC Service
                                  |
                          TLS termination +
                          client cert validation

Prerequisites

Before you begin, make sure you have:

Step 1: Deploy a sample gRPC server

Deploy a gRPC server Deployment and its corresponding Service to the ACK cluster.

  1. Log on to the ACK console. In the left-side navigation pane, click Clusters.

  2. On the Clusters page, click the name of the cluster that you want to manage and choose Workloads > Deployments in the left-side navigation pane.

  3. In the upper part of the Deployments page, select a namespace from the Namespace drop-down list and click Create from YAML.

  4. On the Create page, copy the following YAML code to the code editor and click Create.

    Expand to view the YAML code

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: istio-grpc-server-v1
      labels:
        app: istio-grpc-server
        version: v1
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: istio-grpc-server
          version: v1
      template:
        metadata:
          labels:
            app: istio-grpc-server
            version: v1
        spec:
          containers:
          - args:
            - --address=0.0.0.0:8080
            image: registry.cn-hangzhou.aliyuncs.com/aliacs-app-catalog/istio-grpc-server
            imagePullPolicy: Always
            livenessProbe:
              exec:
                command:
                - /bin/grpc_health_probe
                - -addr=:8080
              initialDelaySeconds: 2
            name: istio-grpc-server
            ports:
            - containerPort: 8080
            readinessProbe:
              exec:
                command:
                - /bin/grpc_health_probe
                - -addr=:8080
              initialDelaySeconds: 2
    ---
    apiVersion: v1
    kind: Service
    metadata:
      name: istio-grpc-server
      labels:
        app: istio-grpc-server
    spec:
      ports:
      - name: grpc-backend
        port: 8080
        protocol: TCP
      selector:
        app: istio-grpc-server
      type: ClusterIP
    Important

    The name field under ports in the Service definition must start with http2- or grpc-. Istio uses this prefix to identify the protocol. If the name does not follow this convention, Istio treats the traffic as plain TCP and routing may not work as expected.

Step 2: Create an ingress gateway

Use the default port 443 to expose the gRPC service externally. For detailed steps, see Create an ingress gateway.

Step 3: Configure a Gateway and a VirtualService

Create an Istio Gateway and a VirtualService to route HTTPS/gRPC traffic on port 443 to the backend gRPC server.

Create the Gateway

  1. Log on to the ASM console. In the left-side navigation pane, choose Service Mesh > Mesh Management.

  2. On the Mesh Management page, find the ASM instance that you want to configure, and click the instance name or click Manage in the Actions column.

  3. Create an Istio gateway.

    1. In the left-side navigation pane, choose ASM Gateways > Gateway.

    2. On the Gateway page, click Create from YAML.

    3. Select default from the Namespace drop-down list, copy the following YAML code to the code editor, and click Create.

      apiVersion: networking.istio.io/v1beta1
      kind: Gateway
      metadata:
        name: gw-grpc-443
        namespace: default
      spec:
        selector:
          istio: ingressgateway
        servers:
          - hosts:
              - '*'
            port:
              name: https
              number: 443
              protocol: HTTPS
            tls:
              credentialName: example-credential
              mode: MUTUAL
  4. Key fields:

    FieldValueDescription
    tls.modeMUTUALEnables mTLS. The gateway requires a valid client certificate for every connection.
    tls.credentialNameexample-credentialReferences the Kubernetes Secret that stores the server certificate and the CA certificate for client verification. You create this Secret in Step 4.

Create the VirtualService

  1. In the left-side navigation pane of the ASM instance, choose Traffic Management Center > VirtualService.

  2. On the VirtualService page, click Create from YAML.

  3. Select default from the Namespace drop-down list, copy the following YAML code to the code editor, and click Create.

    This VirtualService routes all traffic arriving on port 443 through the gw-grpc-443 Gateway to the istio-grpc-server backend.

    apiVersion: networking.istio.io/v1alpha3
    kind: VirtualService
    metadata:
      name: grpc-vs
    spec:
      hosts:
      - "*"
      gateways:
      - gw-grpc-443
      http:
        - match:
            - port: 443
          route:
            - destination:
                host: istio-grpc-server

Step 4: Mount TLS certificates

The ingress gateway needs a server certificate for TLS termination and a CA certificate to verify client certificates. A gRPC client typically requires a Subject Alternative Name (SAN) certificate.

Important

The Secret name must match the credentialName value configured in the Gateway resource. In this example, both use example-credential.

Generate test certificates

For testing, generate a self-signed CA and the required server and client certificates. Run the following commands:

# Create a directory for the certificates
mkdir -p example_certs && cd example_certs

# Generate a self-signed CA certificate
openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 \
  -subj '/O=example Inc./CN=example.com' \
  -keyout ca.key -out ca.crt

# Generate a server certificate signed by the CA
openssl req -newkey rsa:2048 -nodes \
  -subj '/CN=x.test.example.com/O=server' \
  -keyout server_key.pem -out server.csr
openssl x509 -req -sha256 -days 365 \
  -CA ca.crt -CAkey ca.key -set_serial 0 \
  -in server.csr -out server_cert.pem

# Generate a client certificate signed by the same CA
openssl req -newkey rsa:2048 -nodes \
  -subj '/CN=x.test.example.com/O=client' \
  -keyout client_key.pem -out client.csr
openssl x509 -req -sha256 -days 365 \
  -CA ca.crt -CAkey ca.key -set_serial 1 \
  -in client.csr -out client_cert.pem

Alternatively, use the sample certificates from the grpc-go examples.

Mount certificates for ASM versions earlier than 1.17

Run the following kubectl command to create a Secret in the istio-system namespace, where the ingress gateway runs:

kubectl create -n istio-system secret generic example-credential \
  --from-file=tls.key=server_key.pem \
  --from-file=tls.crt=server_cert.pem \
  --from-file=ca.crt=client_ca_cert.pem
Important

If you generated certificates by using the commands in Generate test certificates, replace client_ca_cert.pem with ca.crt in the preceding command.

The following table describes the Secret keys:

KeyFileDescription
tls.keyserver_key.pemServer private key
tls.crtserver_cert.pemServer certificate
ca.crtclient_ca_cert.pemCA certificate used to verify client certificates. Use ca.crt if you generated your own certificates.

Mount certificates for ASM version 1.17 or later

  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 > Certificate Management.

  3. On the Certificate Management page, click Create. In the Certificate Information panel, configure the required parameters and click OK.

Step 5: Test the gRPC client with mTLS

With the Gateway, VirtualService, and certificates in place, verify the setup by running a gRPC client that presents a client certificate.

Install dependencies

Set up the Go gRPC development environment. See Quick start of gRPC in Go.

Clone the sample repository

Clone the grpc-go repository, which includes sample certificates and the helloworld example:

git clone https://github.com/grpc/grpc-go.git

The sample certificates are located at grpc-go/examples/data/x509/.

Update the client code

Replace the content of grpc-go/examples/helloworld/greeter_client/main.go with the following code. Set the addr flag to <ingress-gateway-ip>:443.

Expand to view the sample code

/*
 *
 * Copyright 2015 gRPC authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// Package main implements a client for Greeter service.
package main

import (
    "context"
    "crypto/tls"
    "crypto/x509"
    "flag"
    "io/ioutil"
    "log"
    "time"

    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
    pb "google.golang.org/grpc/examples/helloworld/helloworld"
)

const (
    defaultName = "world"
)

var (
    addr       = flag.String("addr", "localhost:50051", "the address to connect to")
    name       = flag.String("name", defaultName, "Name to greet")
    cert       = flag.String("cert", "./data/x509/client_cert.pem", "server cert for mTLS")
    key        = flag.String("key", "./data/x509/client_key.pem", "server key for mTLS")
    cacert     = flag.String("cacert", "./data/x509/ca_cert.pem", "ca cert for mTLS")
    servername = flag.String("servername", "x.test.example.com", "the cert name of server")
)

func main() {
    flag.Parse()

    // Load client certificate and private key
    certPair, err := tls.LoadX509KeyPair(*cert, *key)
    if err != nil {
        log.Fatalf("failed to load client cert: %v", err)
    }

    // Load CA certificate to verify the server
    ca := x509.NewCertPool()
    caFilePath := *cacert
    caBytes, err := ioutil.ReadFile(caFilePath)
    if err != nil {
        log.Fatalf("failed to read ca cert %q: %v", caFilePath, err)
    }
    if ok := ca.AppendCertsFromPEM(caBytes); !ok {
        log.Fatalf("failed to parse %q", caFilePath)
    }

    tlsConfig := &tls.Config{
        ServerName:   *servername,
        Certificates: []tls.Certificate{certPair},
        RootCAs:      ca,
    }

    // Establish a gRPC connection with mTLS credentials
    conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
    if err != nil {
        log.Fatalf("did not connect: %v", err)
    }
    defer conn.Close()
    c := pb.NewGreeterClient(conn)

    // Send an RPC and print the response
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()
    r, err := c.SayHello(ctx, &pb.HelloRequest{Name: *name})
    if err != nil {
        log.Fatalf("could not greet: %v", err)
    }
    log.Printf("Greeting: %s", r.GetMessage())
}

Replace the following placeholder with your actual value:

PlaceholderDescriptionExample
<ingress-gateway-ip>Public IP address of the ingress gateway47.95.XX.XX

Run the client

Navigate to the grpc-go/examples directory and run:

go run helloworld/greeter_client/main.go -addr <ingress-gateway-ip>:443

Expected output:

Greeting: Hello World

Verify that mTLS blocks unauthenticated clients

To confirm that mTLS is enforced, connect without a client certificate. The connection should fail with a TLS handshake error.

Negative test with curl

Run the following command without a client certificate. Replace <ingress-gateway-ip> with the public IP address of your ingress gateway:

curl -v --cacert ca.crt \
  https://<ingress-gateway-ip>:443

Expected result: The TLS handshake fails with an error similar to certificate required or tlsv13 alert certificate required. This confirms the gateway rejects unauthenticated clients.

Positive test with curl

Run the following command with a valid client certificate:

curl -v --cacert ca.crt \
  --cert client_cert.pem --key client_key.pem \
  https://<ingress-gateway-ip>:443

Expected result: The TLS handshake succeeds and the gateway accepts the connection.

Troubleshooting

SymptomPossible causeSolution
TLS handshake error on the clientThe server certificate in the Secret is invalid or expired.Recreate the Secret with valid certificate files. Make sure the Secret name matches the credentialName in the Gateway resource.
connection refused on port 443The ingress gateway is not listening on port 443, or the Gateway resource is not applied.Verify the Gateway resource exists: kubectl get gateway -n default. Check that the ingress gateway pod is running in the istio-system namespace.
certificate required errorThe client is not sending a certificate.Make sure the client code loads both the client certificate and key. Check the file paths passed to the -cert and -key flags.
Traffic reaches the gateway but times outThe VirtualService does not route traffic to the correct backend.Verify the VirtualService destination host matches the Service name (istio-grpc-server). Check that the Service port name starts with grpc- or http2-.
Secret exists but gateway returns TLS errorsThe Secret may not be in the correct namespace or the key names are wrong.Verify the Secret is in the istio-system namespace: kubectl -n istio-system get secrets. Check that the Secret contains tls.key, tls.crt, and ca.crt keys.
Gateway pod logs show no certificateThe ingress gateway cannot find the credential.Check the gateway pod logs: kubectl logs -n istio-system <gateway-pod-name>. Verify that credentialName in the Gateway YAML matches the Secret name.

What's next