In Alibaba Cloud Service Mesh (ASM), you can use Secret Discovery Service (SDS) to improve the security of Istio gateways with HTTPS security support and dynamic certificate loading. This topic shows you how to use SDS to configure a Transport Layer Security (TLS) ingress gateway.

Prerequisites

Background information

The Use an Istio gateway to enable HTTPS topic describes how to use a secret volume to mount certificates to the container of a sidecar proxy. In this case, when the certificate is rotated, you must restart the service so that the sidecar proxy can reload the new certificate. Istio also provides SDS for dynamic certificate loading. You can use SDS to configure the private key, server certificate, and root certificate that are required for TLS.
An ingress gateway proxy runs in the same pod as the ingress gateway and listens to the secret that is created in the namespace of the ingress gateway. Enabling SDS for an ingress gateway brings the following benefits:
  • The ingress gateway can dynamically add, delete, and update the server certificate and private key or the root certificate as required without the need to restart the service.
  • No secret volume is required to be mounted. After a Kubernetes secret is created, the ingress gateway proxy can obtain the secret and send the server certificate and private key or the root certificate to the ingress gateway.
  • If you create secrets for multiple servers and update the ingress gateway, the ingress gateway proxy can listen to multiple server certificates and private key pairs.

Step 1: Enable SDS in an ingress gateway

Note By default, SDS is enabled in ASM instances whose Istio version is 1.6.
  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 of the ASM instance.
  4. On the details page of the ASM instance, click ASM Gateways in the left-side navigation pane.
  5. Find the ingress gateway where you want to enable SDS and click YAML in the Actions column.
  6. Edit the YAML file of the ingress gateway by adding the following content to the spec field:
      sds:
        enabled: true
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 2000m
            memory: 1024Mi
    Note Set the enabled parameter to true and other resource parameters as required.
    The following 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
      sds:
        enabled: true
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 2000m
            memory: 1024Mi
      serviceAnnotations:
        service.beta.kubernetes.io/alibaba-cloud-loadbalancer-spec: slb.s1.small
      serviceType: LoadBalancer

Step 2: Prepare server certificates and private key pairs for multiple servers

Generate a server certificate and private key pair for the aliyun.com domain name and store the pair in a secret.

If you have an available server certificate and private key pair for the aliyun.com domain name, rename the private key to aliyun.com.key and the server certificate to aliyun.com.crt. Alternatively, run the following openssl commands to create a server certificate and private key pair for aliyun.com.

  1. Run the following command to create a root certificate and a private key:
    openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=myexample Inc./CN=aliyun.com' -keyout aliyun.root.key -out aliyun.root.crt 
  2. Run the following commands to generate a server certificate and private key pair for the server of aliyun.com:
    openssl req -out aliyun.com.csr -newkey rsa:2048 -nodes -keyout aliyun.com.key -subj "/CN=aliyun.com/O=myexample organization"
    openssl x509 -req -days 365 -CA aliyun.root.crt -CAkey aliyun.root.key -set_serial 0 -in aliyun.com.csr -out aliyun.com.crt
  3. Run the following command on the cluster to which the ingress gateway pod belongs to create a secret that contains the server certificate and private key pair in the istio-system namespace:
    kubectl create -n istio-system secret tls myexample-credential --key=aliyun.com.key --cert=aliyun.com.crt
    Notice The name of the secret cannot start with istio or prometheus, and cannot contain the token field.

Step 3: Define an internal service for a.aliyun.com

The internal services in this example are implemented based on NGINX. You must create a configuration file for the NGINX server. Take an internal service of the a.aliyun.com domain name as an example. Define the requested root path to directly return the sentence Welcome to a.aliyun.com!and status code 200. The myexample-nginx.conf file contains the following content:

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 /hello {
        return 200 'Welcome to a.aliyun.com!';
        add_header Content-Type text/plain;
    }
  }
}
  1. In the cluster to which the ingress gateway pod belongs, run the following command to create a ConfigMap for storing the configuration of the NGINX server:
    kubectl create configmap myexample-nginx-configmap --from-file=nginx.conf=./myexample-nginx.conf
  2. In the cluster to which the ingress gateway pod belongs, run the following command to enable automatic sidecar injection for the default namespace:
    kubectl label namespace default istio-injection=enabled
  3. Create a myexampleapp.yaml file that contains the following code. Then, run the kubectl apply -f myexampleapp.yaml command to create an internal service for the a.aliyun.com domain name.
    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

Step 4: Define an internal service for b.aliyun.com

The internal services in this example are implemented based on HTTPBin. Create a httpbin.example.yaml file that contains the following code. Then, run the kubectl apply -f httpbin.example.yaml command on the cluster to which the ingress gateway pod belongs to create an internal service for the b.aliyun.com domain name.

apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
spec:
  ports:
  - name: http
    port: 8000
  selector:
    app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
      version: v1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
    spec:
      containers:
      - image: docker.io/citizenstig/httpbin
        imagePullPolicy: IfNotPresent
        name: httpbin
        ports:
        - containerPort: 8000

Step 5: Define an Istio 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 of the ASM instance.
  4. On the details page of the ASM instance, choose Traffic Management > Gateway in the left-side navigation pane.
  5. On the Gateway page, click Create.
  6. In the Create panel, perform the following steps to define an Istio gateway. Then, click OK.
    1. Select a namespace as required. In this example, the default namespace is used.
    2. In the code editor, define an Istio gateway. The following code shows a sample YAML file:
      apiVersion: networking.istio.io/v1alpha3
      kind: Gateway
      metadata:
        name: mysdsgateway
      spec:
        selector:
          istio: ingressgateway # use istio default ingress gateway
        servers:
        - port:
            number: 443
            name: https
            protocol: HTTPS
          tls:
            mode: SIMPLE
            credentialName: myexample-credential # must be the same as secret
          hosts:
          - '*.aliyun.com'
                                      
    On the Gateway page, you can view the defined Istio gateway.

Step 6: Define a virtual service

  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 of the ASM instance.
  4. On the details page of the ASM instance, choose Traffic Management > VirtualService in the left-side navigation pane.
  5. On the VirtualService page, click Create.
  6. In the Create panel, perform the following steps to define a virtual service. Then, click OK.
    1. Select a namespace as required. In this example, the default namespace is used.
    2. In the code editor, define a virtual service. The following code shows a sample YAML file:
      apiVersion: networking.istio.io/v1alpha3
      kind: VirtualService
      metadata:
        name: mysdsgateway-myexamplevs
      spec:
        hosts:
        - "a.aliyun.com"
        gateways:
        - mysdsgateway
        http:
        - match:
          - uri:
              exact: /hello
          route:
          - destination:
              host: myexampleapp.default.svc.cluster.local
              port:
                number: 80      

      Define a virtual service for httpbin.example.com in a similar way.

      apiVersion: networking.istio.io/v1alpha3
      kind: VirtualService
      metadata:
        name: mysdsgateway-httpbinvs
      spec:
        hosts:
        - "b.aliyun.com"
        gateways:
        - mysdsgateway
        http:
        - match:
          - uri:
              prefix: /status
          - uri:
              prefix: /delay
          route:
          - destination:
              port:
                number: 8000
              host: httpbin.default.svc.cluster.local        
    On the VirtualService page, you can view the defined virtual service.

Execution result

Use one of the following methods to query the IP address of the ingress gateway:

  • View the IP address of the ingress gateway in the ASM console. For more information, see Deploy an ingress gateway service.
  • Run the following command on the cluster to which the ingress gateway pod belongs to query the IP address of the ingress gateway:
    kubectl get svc -n istio-system -l istio=ingressgateway
  • Run the following command to access the aliyun.com domain name over HTTPS:
    curl -k -H Host:a.aliyun.com --resolve a.aliyun.com:443:{IP address of the ingress gateway}  https://a.aliyun.com/hello
    Welcome to aliyun.com!
  • Run the following command to access the httpbin.example.com domain name over HTTPS:
    curl -k -H Host:b.aliyun.com --resolve b.aliyun.com:443:{IP address of the ingress gateway}  https://b.aliyun.com/status/418  
        -=[ teapot ]=-
    
           _...._
         .'  _ _ `.
        | ."` ^ `". _,
        \_;`"---"`|//
          |       ;/
          \_     _/
            `"""`