This topic describes how to deploy a custom ingress gateway in Istio and how to use cert-manager to manage certificates.

Istio supports multiple custom ingress gateways to handle incoming connections at the edge of the mesh through different ports and uses different load balancers to isolate different traffic. cert-manager can be used to obtain certificates by using signature key pairs stored in the Kubernetes Secret resource.


  • You have deployed Istio in the Kubernetes cluster.

    Alibaba Cloud Container Service for Kubernetes now supports quick deployment of Istio. You can Create an ACK cluster and Deploy Istio through the Container Service console.

  • You can connect to the Kubernetes cluster through CloudShell.
  • To make it easier to manage the cluster, we recommend that you include the gateway name in secretName. Specifically, secretName must be istio-{Gateway Name}-certs or istio-{Gateway Name}-ca-cert.

Enable cert-manager

  1. In the default Istio configuration, cert-manager is not enabled for certificate management. You can enable cert-manager after Istio is installed.
  2. Log on to the Container Service console.
  3. In the left-side navigation pane, chooseService Mesh > Istio Management to go to the management page.
  4. Click Update in the upper-right corner. In the dialog box that appears, modify the parameters. Set the value of certManager.enabled to true and click Update.
    Note If you cannot find the certManager parameter, you can add it as the first line in YAML format.

Generate a signature key pair

CA Issuer does not automatically create or manage signature key pairs. You can use either of the following methods to obtain a signature key pair:
  • If you already have your own key pair, you need to name the private key and the certificate as ca.key and ca.crt respectively.
  • If you have no signature key pair, you can generate a key and certificate of type x509 by using the following steps.
    1. Run the following command to generate a CA key:
      docker run -it -v $(pwd):/export frapsoft/openssl genrsa -out /export/ca.key 2048
    2. Run the following command to generate a self-signed certificate:
      docker run -it -v $(pwd):/export frapsoft/openssl req -x509 -new -nodes -key /export/ca.key -subj "/" -days 3650 -reqexts v3_req -extensions v3_ca -out /export/ca.crt

Save the signature key pair as a Secret

Run the following commands to create a Secret that contains a signature key pair in the default namespace:
kubectl create namespace myexample
kubectl create secret tls istio-myexample-customingressgateway-ca-certs \
   --cert=ca.crt \
   --key=ca.key \
  • Create an Issuer that uses this key pair to generate signed certificates. To ensure that the Issuer can reference the key pair, store the key pair in a Kubernetes Secret resource.
  • Issuers are scoped to namespaces and can only reference Secrets in their own namespaces. Therefore, make sure that the key pair is in the same namespace as the Issuer.

Create an Issuer to reference the Secret

Create a certmanager-secret-issuer.yaml file with the following content. Then run the kubectl apply -n myexample -f certmanager-secret-issuer.yaml command to create an Issuer that uses the key pair to generate signed certificates.
kind: Issuer
  name: ca-issuer
  namespace: myexample
    secretName: istio-myexample-customingressgateway-ca-certs

Obtain a signed certificate

You can now create the following Certificate resource, which specifies the required certificate. To use an Issuer to obtain a certificate, you must create the Certificate resource in the same namespace as the Issuer. Issuers are divided into namespaces, as shown in this example. If you want to reuse signature key pairs across multiple namespaces, you can use a ClusterIssuer. First, run the following command to create a certificate for the domain name

Create a myexample-certificate.yaml file with the following content. Then run the kubectl apply -n myexample -f myexample-certificate.yaml command to create a signed certificate.
kind: Certificate
  name: myexample-certificate
  namespace: myexample
  secretName: istio-myexample-customingressgateway-certs
    name: ca-issuer
    # You can reference an Issuer of the ClusterIssuer type. By default, use the Issuer of the namespace only.
    kind: Issuer
  - MyExample CA

After the Certificate resource is created, cert-manager attempts to use the ca-issuer issuer to obtain a certificate. If the attempt is successful, the certificate is stored inside a Secret resource named istio-myexample-customingressgateway-certs, in the same namespace (myexample) as the Certificate resource.

Check the certificate and key

Because the commonName field is already specified, will be the common name of the certificate. Both the common name and all the elements in the dnsNames list will be subject alternative names (SANs). If the common name is not specified, the first element in the dnsNames list will be used as the common name, and all the elements in the dnsNames list will also be SANs.

After the certificate is created, run the following command to check whether the certificate has been obtained.

Check the myexample-certificate certificate as follows:

kubectl describe certificate myexample-certificate -n myexample
Name:         myexample-certificate
Namespace:    default
Labels:       <none>
API Version:
Kind:         Certificate
  Common Name:
  Dns Names:
  Issuer Ref:
    Kind:  Issuer
    Name:  ca-issuer
    MyExample CA
  Secret Name:  istio-myexample-customingressgateway-certs
    Message:               Certificate is up to date and has not expired
    Reason:                Ready
    Status:                True
    Type:                  Ready
  Type    Reason      Age   From          Message
  ----    ------      ----  ----          -------
  Normal  CertIssued  78s   cert-manager  Certificate issued successfully

If the last line shows Certificate issued successfully, it indicates that the certificate has been successfully created.

You can also run the kubectl get secret istio-myexample-customingressgateway-certs -o yaml command to check whether the issuance was successful. If successful, you can see a base64 encoded signed TLS key pair.

After the certificate has been obtained, cert-manager will continue to check its validity and attempt to update the certificate when it is about to expire. For example, when the Not After field on the certificate is less than the current time plus 30 days, cert-manager considers the certificate to be close to expiry. For CA-based Issuers, cert-manager will issue certificates with the Not After field set to the current time plus 365 days.

Deploy a custom gateway

Gateway describes a load balancer that operates at the edge of the mesh receiving incoming or outgoing HTTP/TCP connections. Alibaba Cloud Container Service for Kubernetes supports managing custom gateways through CRD. The following YAML content defines a custom ingress gateway:

kind: IstioGateway
  labels: "1.0"  
  name: "myexample-customingressgateway"  
  maxReplicas: 5  
  minReplicas: 1  
  - name: status-port    
    port: 15020    
    targetPort: 15020  
  - name: http2    
    port: 80    
    targetPort: 80  
  - name: https    
    port: 443    
    targetPort: 0  
  - name: tls    
    port: 15443    
    targetPort: 15443  
  replicaCount: 1  
  - name: myexample-customingressgateway-certs
    secretName: istio-myexample-customingressgateway-certs
    mountPath: /etc/istio/myexample-customingressgateway-certs
  - name: myexample-customingressgateway-ca-certs
    secretName: istio-myexample-customingressgateway-ca-certs
    mountPath: /etc/istio/myexample-customingressgateway-ca-certs
  serviceType: LoadBalancer  
  serviceAnnotations: internet  
Make sure that the gateway name is included in secretName. Specifically, secretName must be istio-{Gateway Name}-certs or istio-{Gateway Name}-ca-certs.

Create a myexample-customingressgateway.yaml file with the following content. Then run the kubectl apply -n myexample -f myexample-customingressgateway.yaml command to create a signed certificate.

Define internal services

The internal services in this example are implemented based on NGINX. First, create a configuration file for the NGINX server. Take the internal services of the domain as an example. Define the requested root path to directly return the sentence "Welcome to! This is one custom Istio Ingress Gateway powered by cert-manager!" and the 200 status code.

The specific content of myexample-nginx.conf is as follows:
events {

http {
  log_format main '$remote_addr - $remote_user [$time_local]  $status '
  '"$request" $body_bytes_sent "$http_referer" '
  '"$http_user_agent" "$http_x_forwarded_for"';
  access_log /var/log/nginx/access.log main;
  error_log  /var/log/nginx/error.log;

  server {
    listen 80;

    location / {
        return 200 'Welcome to! This is one custom Istio Ingress Gateway powered by cert-manager!' ;
        add_header Content-Type text/plain;
  1. Run the following command to create a Kubernetes ConfigMap to store the configuration of the Nginx server.
    kubectl create configmap myexample-nginx-configmap --from-file=nginx.conf=./myexample-nginx.conf
  2. Run the following command to set the namespace to default and start automatic sidecar injection:
    kubectl label namespace default istio-injection=enabled
    Note Make sure that labeling for automatic sidecar injection only occurs after IngressGateway has been created. This ensures that labels are not automatically injected into IngressGateway. You may also choose not to start automatic injection and to complete the process using manual injection. For more information, see Manual injection.
  3. Create a myexampleapp.yaml file with the following content. Then run the kubectl apply -f myexampleapp.yaml command to create an internal service for the domain name
    apiVersion: v1
    kind: Service
      name: myexampleapp
        app: myexampleapp
      - port: 80
        protocol: TCP
        app: myexampleapp
    apiVersion: apps/v1
    kind: Deployment
      name: myexampleapp
          app: myexampleapp
      replicas: 1
            app: myexampleapp
          - name: nginx
            image: nginx
            - containerPort: 80
            - name: nginx-config
              mountPath: /etc/nginx
              readOnly: true
          - name: nginx-config
              name: myexample-nginx-configmap

Create a custom gateway configuration object

Create an istio-myexample-customingressgateway.yaml file with the following content. Then run the kubectl apply -n myexample -f istio-myexample-customingressgateway.yaml command to create an Istio custom gateway configuration object.
kind: Gateway
  name: istio-myexample-customingressgateway
    istio: ingressgateway
  - hosts:
    - '*'
      name: http
      number: 80
      protocol: HTTP
      httpsRedirect: true
  - hosts:
    - '*'
      name: https
      number: 443
      protocol: HTTPS
      mode: SIMPLE
      privateKey: /etc/istio/myexample-customingressgateway-certs/tls.key
      serverCertificate: /etc/istio/myexample-customingressgateway-certs/tls.crt

Create a VirtualService

Create an istio-myexample-customvirtualservice.yaml file with the following content. Then run the kubectl apply -n myexample -f istio-myexample-customvirtualservice.yaml command to create a VirtualService that connects to istio-myexample-customingressgateway.
kind: VirtualService
  name: istio-myexample-customvirtualservice
  - ""
  - istio-myexample-customingressgateway
  - route:
    - destination:
        host: myexampleapp.default.svc.cluster.local
          number: 80

Access services using a gateway

  1. Take the domain name as an example. Run the kubectl get svc -n myexample -l istio=myexample-customingressgateway command to obtain the public IP address of the corresponding custom gateway service:
    NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                      AGE
    istio-myexample-customingressgateway   LoadBalancer   172.19.XX.XX   106.14.XX.XX    ....   11m
    Note Change environment variables INGRESS_HOST and SECURE_INGRESS_PORT to the address values of the actual environment, which can be found in the EXTERNAL-IP value from step 1.
  2. Run the following command to check whether the istio-ingressgateway Pod has correctly loaded the certificate and private key:
    kubectl exec -it -n myexample $(kubectl -n myexample get pods -l istio=myexample-customingressgateway -o jsonpath='{.items[0]}') -- ls -al /etc/istio/myexample-customingressgateway-certs

    Both tls.crt and tls.key are expected to be saved in this directory.

  3. Run the following command to check whether the Subject field in the Ingress gateway certificate is correct.
    kubectl exec -i -n myexample $(kubectl get pod -l istio=myexample-customingressgateway  -n myexample -o jsonpath='{.items[0]}')  -- cat /etc/istio/myexample-customingressgateway-certs/tls.crt | openssl x509 -text -noout | grep 'Subject:'
            Subject: O=MyExample CA,
  4. Run the following command to check whether the Ingress gateway proxy is able to correctly access the certificate:
    kubectl exec -ti $(kubectl get po -l istio=myexample-customingressgateway -n myexample -o jsonpath={.items[0]}) -n myexample -- curl
        "ca_cert": "",
        "cert_chain": "...."

You have now completed all the steps for using cert-manager to deploy a custom ingress gateway.

Expected result

Run the following command to access the service over HTTPS.
curl -k -H --resolve
Welcome to! This is one custom Istio Ingress Gateway powered by cert-manager!