Set up TLS termination in ingress gateway

Overview

This page demonstrates how to set up a TLS termination in ingress gateway in Cloud Service Mesh to manage external HTTPS traffic to your services. For a more basic introduction on how to setup gateways, refer to the gateways guide. You will learn how to configure the gateway for secure communication using TLS, enabling encrypted access to your applications. This process leverages Cloud Service Mesh capabilities to expose services securely.

Before you begin

You need the following resources to complete the steps in this document:

  • A Kubernetes cluster with Cloud Service Mesh installed. See the installation guide for details on how to install Cloud Service Mesh.

Set up your environment

Run the following commands from a workstation that can access the cluster you intend to use. Make sure that the kubectl tool is configured to use the cluster context specific to your cluster.

  1. Set the environment variables.

    export CSM_INGRESSGATEWAY_NAMESPACE=CSM_INGRESSGATEWAY_NAMESPACE export CSM_INGRESSGATEWAY_DEPLOYMENT_NAME=CSM_INGRESSGATEWAY_DEPLOYMENT_NAME export CSM_INGRESSGATEWAY_SERVICE_NAME=CSM_INGRESSGATEWAY_SERVICE_NAME 
  2. Deploy the foo application in your cluster. Install it with the following yaml:

    apiVersion: v1 kind: Service metadata:  name: foo  namespace: foo spec:  selector:  app: test-backend  ports:  - port: 8080  targetPort: 8080 --- apiVersion: apps/v1 kind: Deployment metadata:  name: foo  namespace: foo spec:  replicas: 2  selector:  matchLabels:  app: test-backend  template:  metadata:  labels:  app: test-backend  spec:  containers:  - name: whereami  image: gcr.io/google-samples/whereami:v1.2.23  ports:  - containerPort: 8080 EOF 
  3. Generate certificates and keys.

    To secure your ingress gateway, you will need TLS certificates and keys. You can use any certificate generation tool or follow these steps using openssl to create the necessary credentials.

    • Create a root CA certificate and key
    mkdir example_certs openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=Example Corp/CN=example.com' \  -keyout example.com.key -out example.com.crt 
    • Generate a certificate and key for ingress
    openssl req -out foo.example.com.csr -newkey rsa:2048 -nodes \  -keyout foo.example.com.key -subj "/CN=foo.example.com/O=Foo Org" openssl x509 -req -sha256 -days 365 -CA example.com.crt \  -CAkey example.com.key -set_serial 0 \  -in foo.example.com.csr -out foo.example.com.crt 

Store the TLS certificate

Store the TLS certificate as a Secret.

  1. Create the namespace. This namespace is used to deploy the ingress gateway.

    kubectl create namespace ${CSM_INGRESSGATEWAY_NAMESPACE} 
  2. Apply the default injection label to the namespace:

    kubectl label namespace ${CSM_INGRESSGATEWAY_NAMESPACE} \  istio.io/rev- istio-injection=enabled --overwrite 
  3. Store the TLS credentials in a Kubernetes secret:

    kubectl create -n ${CSM_INGRESSGATEWAY_NAMESPACE} secret tls foo-credential \  --key=example_certs/foo.example.com.key \  --cert=example_certs/foo.example.com.crt 

Apply the TLS certificate to a gateway

There are two ways to get the gateway to use the newly created TLS certificate.

Deployment with mounted credentials (Preferred)

  1. Copy the default ingress gateway manifest into a local file.

    curl https://raw.githubusercontent.com/GoogleCloudPlatform/anthos-service-mesh-samples/main/docs/ingress-gateway-external-lb/ingress-gateway.yaml > ingress-gateway.yaml 
  2. Amend the deployment spec in ingress-gateway.yaml to mount the TLS secret credential.

    apiVersion: apps/v1 kind: Deployment ... spec:  ...  spec:  ...  volumeMounts:  - name: foo-credential # Add new volume mount specifying mount path.  mountPath: /etc/secrets/foo-credential   readOnly: true   volumes:  - name: foo-credential # Point volume mount to the Kubernetes secret holding the TLS certificate and keys.  secret:  secretName: foo-credential 

    Then create the resources pertaining to the ingress gateway.

    kubectl --namespace ${CSM_INGRESSGATEWAY_NAMESPACE} apply --filename ingress-gateway.yaml  
  3. Define the ingress gateway.

    Create a Gateway resource to handle HTTPS traffic on port 443 that references the mounted secrets:

    cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1 kind: Gateway metadata:  name: secure-gateway  namespace: ${CSM_INGRESSGATEWAY_NAMESPACE} spec:  selector:  app: asm-ingressgateway  istio: ingressgateway  servers:  - port:  number: 443  name: https  protocol: HTTPS  tls:  mode: SIMPLE  serverCertificate: /etc/secrets/foo-credential/foo.example.com.crt  privateKey: /etc/secrets/foo-credential/foo.example.com.key  hosts:  - "foo.example.com" EOF 

Deployment without mounted credentials

  1. Apply the ingress gateway manifest file.

    kubectl --namespace ${CSM_INGRESSGATEWAY_NAMESPACE} apply --filename https://raw.githubusercontent.com/GoogleCloudPlatform/anthos-service-mesh-samples/main/docs/ingress-gateway-external-lb/ingress-gateway.yaml 

    Expected output:

    serviceaccount/asm-ingressgateway created role.rbac.authorization.k8s.io/asm-ingressgateway created rolebinding.rbac.authorization.k8s.io/asm-ingressgateway created deployment.apps/asm-ingressgateway created service/asm-ingressgateway created poddisruptionbudget.policy/asm-ingressgateway created horizontalpodautoscaler.autoscaling/asm-ingressgateway created 
  2. Define the ingress gateway.

    Create a Gateway resource to handle HTTPS traffic on port 443:

    cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1 kind: Gateway metadata:  name: secure-gateway  namespace: ${CSM_INGRESSGATEWAY_NAMESPACE} spec:  selector:  app: asm-ingressgateway  istio: ingressgateway  servers:  - port:  number: 443  name: https  protocol: HTTPS  tls:  mode: SIMPLE  credentialName: foo-credential  hosts:  - "foo.example.com" EOF 

Optimizing the deployment

If you use the deployment without mounted credentials, the ingress gateway Pods periodically check whether the credentials are updated. By default it takes up to 60 minutes. To change the polling frequency, set the CSM_MIN_K8S_SECRET_REFRESH_INTERVAL and CSM_MAX_K8S_SECRET_REFRESH_INTERVAL environment variables for your ingress gateway Pods deployment.

apiVersion: apps/v1 kind: Deployment metadata:  name: asm-ingressgateway  ... spec:  ...  template:  ...  spec:  containers:  - name: istio-proxy  image: auto  env:  - name: CSM_MIN_K8S_SECRET_REFRESH_INTERVAL  value: "15m" # Half of the default minimum interval  - name: CSM_MAX_K8S_SECRET_REFRESH_INTERVAL  value: "30m" # Half of the default maximum interval  ... 

Test Traffic

  1. Route traffic to the foo service.

    Define a VirtualService to direct traffic to the foo deployment:

    cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1 kind: VirtualService metadata:  name: foo-routing  namespace: ${CSM_INGRESSGATEWAY_NAMESPACE} spec:  hosts:  - "foo.example.com"  gateways:  - secure-gateway  http:  - match:  - uri:  prefix: /status  - uri:  prefix: /delay  route:  - destination:  host: foo  port:  number: 8080 EOF 
  2. Set up the external load balancer to connect with the ingress gateway from the cluster.

  3. Test the secure connection.

    Use the following curl command to verify the setup:

    export EXTERNAL_LB_IP_ADDRESS=EXTERNAL_LB_IP_ADDRESS curl -v -H "Host: foo.example.com" --resolve "foo.example.com:443:$EXTERNAL_LB_IP_ADDRESS" \  --cacert example_certs/example.com.crt "https://foo.example.com:443/ping" 

    Replace EXTERNAL_LB_IP_ADDRESS with ip of external load balancer.

    The output is similar to the following:

     { "cluster_name": "gke-us", "host_header": "34.120.175.141", "pod_name": "whereami-deployment-954cbf78-mtlpf", "pod_name_emoji": "😎", "project_id": "my-project", "timestamp": "2021-11-29T17:01:59", "zone": "us-central1-b" } 

What's next