SSL certificates are a must these days. They helps protect the data being sent between the server and the client by encrypting it, which gives your website more credibility. In this blog post I will explore a couple of different ways you can obtain SSL certificates and configure the Istio Gateway to use them.

In this first part, I will show you how to manually create a self-signed certificate first, followed by obtaining a real SSL certificate and how to set that up as well. As you will see, setting all this up is not too complicated. In the upcoming parts, I will explore the ways you can set everything up, so the certificates get automatically renewed and you don't need to manually renew them.

For you to follow along, you will need an actual, cloud-hosted Kubernetes cluster, because you will need an external IP address you can hook up your domain to. I've tested the steps using the demo profile installation of Istio 1.4.0.

Prerequisites:

Cloud-hosted Kubernetes cluster

Istio 1.4.0 (e.g. istioctl manifest apply --set profile=demo ) with default namespace labelled for Istio sidecar injection

Deploying a sample application

In order to ensure stuff works as it should, you will start by deploying a simple Hello World web application. If you have your own application/service you want to use, feel free to use that as well.

First, you will create a Kubernetes deployment using the learncloudnative/helloworld:0.1.0 image:

kubectl run helloworld --image=learncloudnative/helloworld:0.1.0 --port=3000

Next, create a Kubernetes service for it:

cat <<EOF | kubectl apply -f - apiVersion: v1 kind: Service metadata: name: helloworld labels: run: helloworld spec: ports: - name: http port: 80 targetPort: 3000 selector: run: helloworld EOF

Note: if you're wondering why I am not using kubectl expose command, it is because you need to name your ports in Kubernetes services (e.g. http ) and you can't do that through the expose command.

To access the service from the external IP, you also need a Gateway resource:

cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: public-gateway spec: selector: istio: ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - '*' EOF

Note: the hosts field has a value of * - this will change once we create the SSL certificate for the domain name. This is where the domain name will go.

Also create a VirtualService that routes the traffic to the helloworld Kubernetes service:

cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: helloworld spec: hosts: - '*' gateways: - public-gateway http: - route: - destination: host: helloworld.default.svc.cluster.local port: number: 80 EOF

With all these resource deployed, you can now get the external IP of the Istio's ingress gateway:

kubectl get svc -l istio=ingressgateway -n istio-system

If you open the IP under the EXTERNAL-IP column, you will see something similar to the figure below.

You did get a response back from the applicaiton, but you also got the Not Secure message from the browser which tells the user that the connection is not secure and doesn't instill a lot of confidence in your website.

Self-signed certs and manual setup

Let's start with the simplest scenario where we manually obtain the certificates. First thing - pick a domain you want to use - note that to test this, you don't actually have to own the domain name:

export DOMAIN_NAME=mysuperdomain.com

As a first step, you are going to create the root certificate ( $DOMAIN_NAME.crt ) and the private key used for signing the certificate ( $DOMAIN_NAME.key ):

openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj '/O=$DOMAIN_NAME Inc./CN=$DOMAIN_NAME' -keyout $DOMAIN_NAME.key -out $DOMAIN_NAME.crt

Next, you need to create the private key:

$ openssl req -out helloworld.$DOMAIN_NAME.csr -newkey rsa:2048 -nodes -keyout helloworld.$DOMAIN_NAME.key -subj "/CN=helloworld.$DOMAIN_NAME/O=hello world from $DOMAIN_NAME" Generating a 2048 bit RSA private key ....................................+++ ..............................................+++ writing new private key to 'helloworld.learnservicemesh.com.key'

And the certificate:

$ openssl x509 -req -days 365 -CA $DOMAIN_NAME.crt -CAkey $DOMAIN_NAME.key -set_serial 0 -in helloworld.$DOMAIN_NAME.csr -out helloworld.$DOMAIN_NAME.crt Signature ok subject=/CN=helloworld.learnservicemesh.com/O=hello world from learnservicemesh.com Getting CA Private Key

Now that you have the certificate and the key, you can create the secret that holds the certificate and the key. Secret with certificates must be called istio-ingressgateway-certs and be deployed in the istio-system namespace. That way, the ingress gateway will load the secret automatically.

kubectl create secret tls istio-ingressgateway-certs -n istio-system --key helloworld.$DOMAIN_NAME.key --cert helloworld.$DOMAIN_NAME.crt

With the secret in place, you need to update the Gateway resource to tell it to use these certificates:

cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: public-gateway spec: selector: istio: ingressgateway # use istio default ingress gateway servers: - port: number: 443 name: https protocol: HTTPS tls: mode: SIMPLE # These are coming from the istio-ingressgateway-certs secret serverCertificate: /etc/istio/ingressgateway-certs/tls.crt privateKey: /etc/istio/ingressgateway-certs/tls.key hosts: - helloworld.$DOMAIN_NAME EOF

Similarly, you need to update the hosts field in the VirtualService:

cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: helloworld spec: hosts: - helloworld.$DOMAIN_NAME gateways: - public-gateway http: - route: - destination: host: helloworld.default.svc.cluster.local port: number: 80 EOF

The simplest way to test that this works is to use curl with the --resolve flag. The resolve flag has a format of [DOMAIN]:[PORT]:[IP] and it routes all requests that match the [DOMAIN]:[PORT] portion to the specified IP address. This way you don't need to go to your DNS/domain registrar and make changes there just to be able to test this. The IP address in our case is the external IP address of the ingress gateway:

export EXTERNAL_IP=$(kubectl get svc istio-ingressgateway -n istio-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

Here's the curl command you can use to test that the SSL certs gets verified and used:

curl -v --resolve helloworld.$DOMAIN_NAME:443:$EXTERNAL_IP --cacert $DOMAIN_NAME.crt https://helloworld.$DOMAIN_NAME

With the above command we are telling curl to resolve any requests to helloworld.learnservicemesh.com:443 to the external IP address of the ingress gateway. Additionally, we are providing the name of the CA certificate we created earlier.

From the output you will be able to see the details of the server certificate and a line that says certificate was verified as well as the actual response from the helloworld pod:

... * Server certificate: * subject: CN=helloworld.mysuperdomain.com; O=hello world from mysuperdomain.com * start date: Nov 30 22:27:11 2019 GMT * expire date: Nov 29 22:27:11 2020 GMT * common name: helloworld.mysuperdomain.com (matched) * issuer: O=mysuperdomain.com Inc.; CN=mysuperdomain.com * SSL certificate verify ok. ... <link rel="stylesheet" type="text/css" href="css/style.css" /> <div class="container"> Hello World! * Connection #0 to host helloworld.mysuperdomain.com left intact </div>* Closing connection 0

Learn everything about Istio Service Mesh BUY NOW

Real-signed certs and manual setup

The self-signed cert route from the previous section is useful only to kick the tires and test things out. You will need certificates that are signed by an actual certificate authority that your clients can trust. There are a couple of way you can get the SSL certificates, the most popular one being Let's Encrypt. I will be using SSL For Free which uses Let's Encrypt to issue the certificates. If you want to spend money, you can also purchase SSL certificates from your domain registrar or at DigiCert.

In this section I will be use a real domain name and real SSL certificates - this means that if you want to follow along, make sure you have your own domain ready to go. I am using Name.com, but you can use any other domain registrar you'd like.

Now that you have your domain, open SSL for Free to get your SSL certificates:

Enter your domain name e.g. mydomain.com in the text field Click the Create Free SSL Certificate button From the next page, click on the "Manual Verification (DNS)" option - this will require you to login to your account where you registered your domain and create a couple of DNS entries. Click the Manually Verify Domain button and you will see something similar as in the figure below:

At this point login to your domain registrar's website, go to the DNS settings for your domain and add the TXT records as described on the website. If you can, set the TTL to 1 second, but note that some registrars only allow 300 seconds or more. This means that you might have to wait a bit for the changes to be applied.

Set the A name record

While you are logged in to the domain registrars website, make sure you add an A record as well that will point your domain to the external IP address of your cluster. Since you requested a certificate for mydomain.com and www.mydomain.com , your A record should point from mydomain.com to the IP address - i.e. you won't be using the helloworld subdomain as you did previously. If you wanted you could create a wildcard certificate by entering *.mydomain.com in the text field.

Verifying the TXT records

You can click the Verify _acme-challenge.mydomain.com and Verify _acme-challenge.www.mydomain.com links to check if the records have been propagated.

After you've verified the changes are propagated, click the Download SSL certificates button. This will take you to another page where you can login/create an account, so the website will notify you of certificate expiration. At this point you can safely delete the TXT records from your domain.

You can Download All SSL Certificate Files to download a .zip file with everything that was generated for your domain.

There will be three files in the .zip package:

ca_bundle.crt

certificate.crt

private.key

Re-create the secret

Let's delete the existing ingressgateway-certs secret, and create a new one with real certificates:

kubectl delete istio-ingressgateway-certs -n istio-system

You can re-create it now with the real SSL certificate and key you got from the downloaded package:

kubectl create secret tls istio-ingressgateway-certs -n istio-system --key private.key --cert certificate.crt

You also need to update the Gateway and the VirtualService to modify the host names.

Let's update the Gateway first (make sure you update the mydomain.com to your actual domain name):

cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: public-gateway spec: selector: istio: ingressgateway # use istio default ingress gateway servers: - port: number: 443 name: https protocol: HTTPS tls: mode: SIMPLE # These are coming from the istio-ingressgateway-certs secret serverCertificate: /etc/istio/ingressgateway-certs/tls.crt privateKey: /etc/istio/ingressgateway-certs/tls.key hosts: - mydomain.com EOF

Similarly, make a change to the VirtualService:

cat <<EOF | kubectl apply -f - apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: helloworld spec: hosts: - learnservicemesh.com gateways: - public-gateway http: - route: - destination: host: helloworld.default.svc.cluster.local port: number: 80 EOF