cert-manager is a certificate lifecycle management system that can be used to issue and deploy certificates. You can use cert-manager to issue certificates for Alibaba Cloud Service Mesh (ASM) gateways. This way, you can use the ASM gateways to access services over HTTPS. This ensures data transmission security. This topic describes how to use cert-manager to manage certificates for ASM gateways.

Background information

cert-manager allows you to issue self-signed certificates and DNS certificates. You can load the certificates on ASM gateways and use the ASM gateways to access services over HTTPS. The two types of certificates have the following differences:
  • Self-signed certificate: Self-signed certificates can be used only for encryption. They cannot be used for authentication. You can use an ASM gateway on which a self-signed certificate is loaded to access services over HTTPS in command-line tools. However, a self-signed certificate is not trusted by web browsers. A web browser marks HTTPS connections that use a self-signed certificate and displays an error message indicating that the connections have potential risks. Therefore, you cannot use an ASM gateway on which a self-signed certificate is loaded to access services over HTTPS in web browsers.
  • DNS certificate: DNS certificates are issued by Certificate Authorities (CAs) and can be used for both encryption and authentication. Compared with self-signed certificates, DNS certificates provide higher security and are trusted by web browsers. You can use an ASM gateway on which a DNS certificate is loaded to access services over HTTPS in both command-line tools and web browsers.

Install cert-manager in your cluster

  1. Install Helm on your computer. For more information, see Helm.
  2. Use kubectl to connect to your cluster. For more information, see Connect to ACK clusters by using kubectl.
  3. Run the following command to create the cert-manager namespace:
    kubectl create namespace cert-manager
  4. Run the following command to add the cert-manager chart:
    helm repo add jetstack https://charts.jetstack.io
  5. Run the following command to obtain the latest information about the cert-manager chart:
    helm repo update
  6. Run the following command to install cert-manager:
    Notice The version of cert-manager must be compatible with the Kubernetes version. For more information about the mapping between cert-manager versions and Kubernetes versions, see Supported Releases.
    helm install \
      cert-manager jetstack/cert-manager \
      --namespace cert-manager \
      --version v1.1.0 \
      --set installCRDs=true

Scenario 1: Use cert-manager to issue a self-signed certificate

Step 1: Create a self-signed certificate in your cluster

  1. Create the issuer.yaml file and add the following content to the file:
    apiVersion: cert-manager.io/v1
    kind: Issuer
    metadata:
      name: selfsigned
    spec:
      selfSigned: {}
    ---
    apiVersion: cert-manager.io/v1
    kind: Certificate
    metadata:
      name: istio-ingressgateway-certs
    spec:
      isCA: true
      duration: 2160h # 90d
      secretName: istio-ingressgateway-certs
      commonName: istio-ingressgateway-certs
      subject:
        organizations:
        - cluster.local
        - cert-manager
      issuerRef:
        name: selfsigned
        kind: Issuer
        group: cert-manager.io
    ---
    apiVersion: cert-manager.io/v1
    kind: Issuer
    metadata:
      name: istio-ingressgateway-certs
    spec:
      ca:
        secretName: istio-ingressgateway-certs
  2. Run the following command to create a self-signed CA and issue a certificate for workloads:
    kubectl apply -f issuer.yaml -n istio-system
  3. Run the following command to view the certificate:
    kubectl get secret -n istio-system 

    Expected output:

    NAME                                        TYPE                             DATA         AGE
    istio-ingressgateway-certs                  kubernetes.io/tls                3            68m

Step 2: Create a volume that is mounted with the certificate and private key on an ingress gateway

  1. Log on to the ASM console.
  2. In the left-side navigation pane, choose Service Mesh > Mesh Management.
  3. On the Mesh Management page, find the ASM instance that you want to configure. Click the name of the ASM instance or click Manage in the Actions column.
  4. On the details page of the ASM instance, click ASM Gateways in the left-side navigation pane.
  5. On the ASM Gateways page, click YAML in the Actions column of the ingress gateway that you want to manage.
  6. Edit the YAML file of the ingress gateway by adding the following content to the spec field:
      secretVolumes:
        - mountPath: /etc/istio/ingressgateway-certs
          name: ingressgateway-certs
          secretName: istio-ingressgateway-certs

    The following sample code provides an example of the edited YAML file:

    apiVersion: istio.alibabacloud.com/v1beta1
    kind: IstioGateway
    metadata:
      name: ingressgateway
      namespace: istio-system
    spec:
      clusterIds:
        - c58638b491a5248669b59609e0a17****
      cpu: {}
      externalTrafficPolicy: Local
      maxReplicas: 1
      minReplicas: 1
      ports:
        - name: status-port
          port: 15020
          targetPort: 15020
        - name: http2
          nodePort: 31380
          port: 80
          targetPort: 80
        - name: https
          nodePort: 31390
          port: 443
          targetPort: 443
        - name: tls
          port: 15443
          targetPort: 15443
      replicaCount: 1
      secretVolumes:
        - mountPath: /etc/istio/ingressgateway-certs
          name: ingressgateway-certs
          secretName: istio-ingressgateway-certs
      serviceAnnotations:
        service.beta.kubernetes.io/alibaba-cloud-loadbalancer-spec: slb.s1.small
      serviceType: LoadBalancer

Step 3: Check the configurations of the ingress gateway in the cluster

After you create a volume that is mounted with the certificate and private key on an ingress gateway, the ingress gateway loads the certificate and private key.

  1. Run the following command to check whether the tls.crt and tls.key files are loaded in the pod of the ingress gateway:
    kubectl exec -it -n istio-system $(kubectl -n istio-system get pods -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}') -- ls -al /etc/istio/ingressgateway-certs

    Expected output:

    lrwxrwxrwx 1 root root   14 May  9 01:14 tls.crt -> ..data/tls.crt
    lrwxrwxrwx 1 root root   14 May  9 01:14 tls.key -> ..data/tls.key
  2. Run the following command to check whether the value of the Subject field is correct in the certificate loaded on the ingress gateway:
    kubectl exec -it -n istio-system $(kubectl -n istio-system get pods -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}')  -- cat /etc/istio/ingressgateway-certs/tls.crt | openssl x509 -text -noout | grep 'Subject:'

    Expected output:

    Subject: O = cert-manager + O = cluster.local, CN = istio-ingressgateway-certs
  3. Run the following command to check whether the proxy of the ingress gateway can access the certificate:
    kubectl exec -it -n istio-system $(kubectl -n istio-system get pods -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}') -- curl  127.0.0.1:15000/certs

    Expected output:

    {
     "certificates": [
      {
       "ca_cert": [
        {
         "path": "\u003cinline\u003e",
         "serial_number": "4edcf3",
         "subject_alt_names": [],
         "days_until_expiration": "7251",
         "valid_from": "2022-01-06T06:17:00Z",
         "expiration_time": "2042-01-01T06:22:03Z"
        }
       ],
    ...........

Step 4: Verify that you can access a service over HTTPS

  1. Create a service in the cluster.
    1. Create the myexample-nginx.conf file and add the following content to the file.
      In this example, a service whose domain name is aliyun.com is implemented based on NGINX. You need to create a configuration file for the NGINX server. The following content specifies that the message Welcome to aliyun.com! and the status code 200 are returned for requests to the root path of the service.
      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 aliyun.com!';
              add_header Content-Type text/plain;
          }
        }
      }
    2. Run the following command to create a ConfigMap for the NGINX server:
      kubectl create configmap myexample-nginx-configmap --from-file=nginx.conf=./myexample-nginx.conf
    3. Run the following command to enable automatic sidecar injection for the default namespace:
      kubectl label namespace default istio-injection=enabled
    4. Create the myexampleapp.yaml file and add the following content to the file:
      apiVersion: v1
      kind: Service
      metadata:
        name: myexampleapp
        labels:
          app: myexampleapp
      spec:
        ports:
        - port: 80
          protocol: TCP
        selector:
          app: myexampleapp
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: myexampleapp
      spec:
        selector:
          matchLabels:
            app: myexampleapp
        replicas: 1
        template:
          metadata:
            labels:
              app: myexampleapp
          spec:
            containers:
            - name: nginx
              image: nginx
              ports:
              - containerPort: 80
              volumeMounts:
              - name: nginx-config
                mountPath: /etc/nginx
                readOnly: true
            volumes:
            - name: nginx-config
              configMap:
                name: myexample-nginx-configmap
    5. Run the following command to create an internal service whose domain name is aliyun.com:
      kubectl apply -f myexampleapp.yaml
  2. Use kubectl to connect to your ASM instance. For more information, see Use kubectl to connect to an ASM instance.
  3. Create a gateway in the ASM instance.
    1. Create istio-myexample-customingressgateway.yaml file and add the following content to the file:
      apiVersion: networking.istio.io/v1alpha3
      kind: Gateway
      metadata:
        name: istio-myexample-customingressgateway
      spec:
        selector:
          istio: ingressgateway
        servers:
        - hosts:
          - '*.aliyun.com'
          port:
            name: http
            number: 80
            protocol: HTTP
          tls:
            httpsRedirect: true
        - hosts:
          - '*.aliyun.com'
          port:
            name: https
            number: 443
            protocol: HTTPS
          tls:
            mode: SIMPLE
            privateKey: /etc/istio/ingressgateway-certs/tls.key
            serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
    2. Run the following command to create a gateway:
      kubectl apply -f istio-myexample-customingressgateway.yaml
  4. Create a virtual service in the ASM instance.
    1. Create the istio-myexample-customvirtualservice.yaml file and add the following content to the file:
      apiVersion: networking.istio.io/v1alpha3
      kind: VirtualService
      metadata:
        name: istio-myexample-customvirtualservice
      spec:
        hosts:
        - "www.aliyun.com"
        gateways:
        - istio-myexample-customingressgateway
        http:
        - route:
          - destination:
              host: myexampleapp.default.svc.cluster.local
              port:
                number: 80
    2. Run the following command to create a virtual service:
      kubectl apply -f istio-myexample-customvirtualservice.yaml
  5. Run the following command to obtain the IP address of the ingress gateway in the cluster:
    kubectl get svc -n istio-system -l istio=ingressgateway
  6. Run the following command to access aliyun.com over HTTPS:
    curl -k -H Host:www.aliyun.com --resolve www.aliyun.com:443:{IP address of the ingress gateway}  https://www.aliyun.com

    Expected output:

    Welcome to aliyun.com!

Scenario 2: Use cert-manager to issue a DNS certificate

Prerequisites
  • An Alibaba Cloud domain name with an Internet Content Provider (ICP) filing is available.
  • A webhook is configured in your cluster to add DNS records.
    You can use alidns-webhook to configure a webhook. In this example, run the following command in your cluster to configure a webhook:
    https://gitee.com/godu/helminit/raw/master/cert-manager/alidns-cm-webhook.yaml

Step 1: Create a Secret for the webhook

  1. Create a RAM user.
    1. Log on to the RAM console by using your Alibaba Cloud account.
    2. In the left-side navigation pane of the RAM console, choose Identities > Users.
    3. On the Users page, click Create User.
    4. On the Create User page, set the Logon Name and Display Name parameters, select OpenAPI Call, and then click OK.
      Record the AccessKey ID and AccessKey secret of the RAM user.
  2. Attach the AliyunDNSFullAccess policy to the RAM user.
    1. On the Users page, find the RAM user to which you want to attach the policy and click Add Permissions in the Actions column.
    2. In the Add Permissions panel, enter AliyunDNSFullAccess in the search box on the System Policy tab, click the AliyunDNSFullAccess policy, and then click OK.
  3. Attach a custom policy to the RAM user.
    1. In the left-side navigation pane of the RAM console, choose Permissions > Policies.
    2. On the Policies page, click Create Policy.
    3. On the Create Policy page, click the JSON tab, enter the following content, and then click Next Step:
      {
          "Version": "1",
          "Statement": [
              {
                  "Action": "*",
                  "Resource": "acs:alidns:*:*:domain/#domain-name",
                  "Effect": "Allow"
              },
              {
                  "Action": [
                      "alidns:DescribeSiteMonitorIspInfos",
                      "alidns:DescribeSiteMonitorIspCityInfos",
                      "alidns:DescribeSupportLines",
                      "alidns:DescribeDomains",
                      "alidns:DescribeDomainNs",
                      "alidns:DescribeDomainGroups"
                  ],
                  "Resource": "acs:alidns:*:*:*",
                  "Effect": "Allow"
              }
          ]
      }
                                      

      Replace #domain-name with your own domain name, such as example.com.

    4. Enter a policy name and click OK.
  4. Run the following commands to use Base64 to encode the AccessKey ID and AccessKey secret that are generated in Step 1:
    echo -n <AccessKey ID> | base64
    echo -n <AccessKey Secret>  | base64
  5. Create a Secret in the cluster.
    1. Create the alidns-secret.yaml file and add the following content to the file:
      apiVersion: v1
      kind: Secret
      metadata:
        name: alidns-secret
        namespace: cert-manager
      data:
        access-key: YOUR_ACCESS_KEY_BASE64  # The Base64-encoded AccesssKey ID. 
        secret-key: YOUR_SECRET_KEY_BASE64  # The Base64-encoded AccesssKey secret. 
    2. Create a Secret.
      kubectl apply -f alidns-secret.yaml

Step 2: Create a ClusterIssuer and a certificate in the cluster

  1. Create a ClusterIssuer.
    1. Create the letsencrypt-staging.yaml file and add the following content to the file:
      apiVersion: cert-manager.io/v1
      kind: ClusterIssuer
      metadata:
        name: letsencrypt-staging
      spec:
        acme:
          server: https://acme-staging-v02.api.letsencrypt.org/directory
          privateKeySecretRef:
            name: letsencrypt-staging-account-key
          solvers:
          - dns01:
              webhook:
                groupName: acme.yourcompany.com
                solverName: alidns
                config:
                  region: ""
                  accessKeySecretRef:
                    name: alidns-secret
                    key: access-key
                  secretKeySecretRef:
                    name: alidns-secret
                    key: secret-key
    2. Run the following command to create a ClusterIssuer:
      kubectl apply -f letsencrypt-staging.yaml
  2. Create a certificate.
    1. Create the example-tls.yaml file and add the following content to the file:
      apiVersion: cert-manager.io/v1
      kind: Certificate
      metadata:
        name: example-tls
        namespace: istio-system
      spec:
        # The secretName will store certificate content
        secretName: istio-ingressgateway-certs
        commonName: istio-ingressgateway-certs
        dnsNames:
        # Replace to your real DNS name
        - example.com
        - "*.example.com"
        issuerRef:
          name: letsencrypt-staging
          kind: ClusterIssuer
    2. Run the following command to create a certificate:
      kubectl apply -f example-tls.yaml

      After the ClusterIssuer and certificate are created, cert-manager issues the certificate and private key to the istio-ingressgateway-certs Secret.

Step 3: Create a volume that is mounted with the certificate and private key on an ingress gateway

  1. Log on to the ASM console.
  2. In the left-side navigation pane, choose Service Mesh > Mesh Management.
  3. On the Mesh Management page, find the ASM instance that you want to configure. Click the name of the ASM instance or click Manage in the Actions column.
  4. On the details page of the ASM instance, click ASM Gateways in the left-side navigation pane.
  5. On the ASM Gateways page, click YAML in the Actions column of the ingress gateway that you want to manage.
  6. Edit the YAML file of the ingress gateway by adding the following content to the spec field:
      secretVolumes:
        - mountPath: /etc/istio/ingressgateway-certs
          name: ingressgateway-certs
          secretName: istio-ingressgateway-certs

    The following sample code provides an example of the edited YAML file:

    apiVersion: istio.alibabacloud.com/v1beta1
    kind: IstioGateway
    metadata:
      name: ingressgateway
      namespace: istio-system
    spec:
      clusterIds:
        - c58638b491a5248669b59609e0a17****
      cpu: {}
      externalTrafficPolicy: Local
      maxReplicas: 1
      minReplicas: 1
      ports:
        - name: status-port
          port: 15020
          targetPort: 15020
        - name: http2
          nodePort: 31380
          port: 80
          targetPort: 80
        - name: https
          nodePort: 31390
          port: 443
          targetPort: 443
        - name: tls
          port: 15443
          targetPort: 15443
      replicaCount: 1
      secretVolumes:
        - mountPath: /etc/istio/ingressgateway-certs
          name: ingressgateway-certs
          secretName: istio-ingressgateway-certs
      serviceAnnotations:
        service.beta.kubernetes.io/alibaba-cloud-loadbalancer-spec: slb.s1.small
      serviceType: LoadBalancer

Step 4: Check the configurations of the ingress gateway in the cluster

  1. Run the following command to check whether the tls.crt and tls.key files are loaded in the pod of the ingress gateway:
    kubectl exec -it -n istio-system $(kubectl -n istio-system get pods -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}') -- ls -al /etc/istio/ingressgateway-certs

    Expected output:

    lrwxrwxrwx 1 root root   14 May  9 01:14 tls.crt -> ..data/tls.crt
    lrwxrwxrwx 1 root root   14 May  9 01:14 tls.key -> ..data/tls.key
  2. Run the following command to check whether the value of the Subject field is correct in the certificate loaded on the ingress gateway:
    kubectl exec -it -n istio-system $(kubectl -n istio-system get pods -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}')  -- cat /etc/istio/ingressgateway-certs/tls.crt | openssl x509 -text -noout | grep 'Subject:'

    Expected output:

    Subject: O = cert-manager + O = cluster.local, CN = istio-ingressgateway-certs
  3. Run the following command to check whether the proxy of the ingress gateway can access the certificate:
    kubectl exec -it -n istio-system $(kubectl -n istio-system get pods -l istio=ingressgateway -o jsonpath='{.items[0].metadata.name}') -- curl  127.0.0.1:15000/certs

    Expected output:

    {
     "certificates": [
      {
       "ca_cert": [
        {
         "path": "\u003cinline\u003e",
         "serial_number": "4edcf3",
         "subject_alt_names": [],
         "days_until_expiration": "7251",
         "valid_from": "2022-01-06T06:17:00Z",
         "expiration_time": "2042-01-01T06:22:03Z"
        }
       ],
    ...........

Step 5: Verify that you can access a service over HTTPS

  1. Create a service in the cluster.
    1. Create the myexample-nginx.conf file and add the following content to the file.
      In this example, a service whose domain name is example.com is implemented based on NGINX. You need to create a configuration file for the NGINX server. The following content specifies that the message Welcome to aliyun.com! and the status code 200 are returned for requests to the root path of the service.
      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 aliyun.com!';
              add_header Content-Type text/plain;
          }
        }
      }
    2. Run the following command to create a ConfigMap for the NGINX server:
      kubectl create configmap myexample-nginx-configmap --from-file=nginx.conf=./myexample-nginx.conf
    3. Run the following command to enable automatic sidecar injection for the default namespace:
      kubectl label namespace default istio-injection=enabled
    4. Create the myexampleapp.yaml file and add the following content to the file:
      apiVersion: v1
      kind: Service
      metadata:
        name: myexampleapp
        labels:
          app: myexampleapp
      spec:
        ports:
        - port: 80
          protocol: TCP
        selector:
          app: myexampleapp
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        name: myexampleapp
      spec:
        selector:
          matchLabels:
            app: myexampleapp
        replicas: 1
        template:
          metadata:
            labels:
              app: myexampleapp
          spec:
            containers:
            - name: nginx
              image: nginx
              ports:
              - containerPort: 80
              volumeMounts:
              - name: nginx-config
                mountPath: /etc/nginx
                readOnly: true
            volumes:
            - name: nginx-config
              configMap:
                name: myexample-nginx-configmap
    5. Run the following command to create an internal service whose domain name is example.com:
      kubectl apply -f myexampleapp.yaml
  2. Use kubectl to connect to your ASM instance. For more information, see Use kubectl to connect to an ASM instance.
  3. Create a gateway in the ASM instance.
    1. Create istio-myexample-customingressgateway.yaml file and add the following content to the file:
      apiVersion: networking.istio.io/v1alpha3
      kind: Gateway
      metadata:
        name: istio-myexample-customingressgateway
      spec:
        selector:
          istio: ingressgateway
        servers:
        - hosts:
          - '*.example.com'
          port:
            name: http
            number: 80
            protocol: HTTP
          tls:
            httpsRedirect: true
        - hosts:
          - '*.example.com'
          port:
            name: https
            number: 443
            protocol: HTTPS
          tls:
            mode: SIMPLE
            privateKey: /etc/istio/ingressgateway-certs/tls.key
            serverCertificate: /etc/istio/ingressgateway-certs/tls.crt
    2. Run the following command to create a gateway:
      kubectl apply -f istio-myexample-customingressgateway.yaml
  4. Create a virtual service in the ASM instance.
    1. Create the istio-myexample-customvirtualservice.yaml file and add the following content to the file:
      apiVersion: networking.istio.io/v1alpha3
      kind: VirtualService
      metadata:
        name: istio-myexample-customvirtualservice
      spec:
        hosts:
        - "www.example.com"
        gateways:
        - istio-myexample-customingressgateway
        http:
        - route:
          - destination:
              host: myexampleapp.default.svc.cluster.local
              port:
                number: 80
    2. Run the following command to create a virtual service:
      kubectl apply -f istio-myexample-customvirtualservice.yaml
  5. Visit example.com in a web browser.
    If the following page appears, you can access the service over HTTPS. Service