avatar
kezhenxu94
Blogging about dev, tips & tricks, tutorials and more
Published on

Adding free HTTPS/SSL certificate to Kubernetes service with Istio, Let's Encrypt and cert-manager

Before we start the tutorial, I will assuming that you have a running Kubernetes cluster, with the ability to allocate a public IP address to your service, because we will need it to be accessible from the internet.

Install Istio

Let's start by installing Istio in your Kubernetes cluster. You can follow the guide on the official Istio website but here is the command to install Istio with the default profile:

istioctl install -y

After several minutes, you should be able to see the Istio ingress gateway service running in the istio-system namespace with a public IP address:

kubectl -n istio-system get svc istio-ingressgateway
NAME                   TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                                      AGE
istio-ingressgateway   LoadBalancer   10.3.252.105   34.150.16.112   15021:30694/TCP,80:31846/TCP,443:30717/TCP   2m47s

Configure DNS record for your domain

In the last step, we have obtained the public IP address of the Istio ingress gateway service, now we need to configure a DNS record for your domain to point to this IP address.

In this tutorial, I'm going to use the domain letsencrypt.kezhenxu94.me as an example, you should replace it with your own domain name.

You can do this by adding an A record to your domain's DNS settings, with the value of the public IP address, I'm using Cloudflare as my DNS provider, so here is how I do it:

Cloudflare DNS record

Install cert-manager

cert-manager is a powerful and extensible X.509 certificate controller for Kubernetes and OpenShift workloads. It will obtain certificates from a variety of Issuers, both popular public Issuers as well as private Issuers, and ensure the certificates are valid and up-to-date, and will attempt to renew certificates at a configured time before expiry.

We will obtain and re-new certificates by leveraging cert-manager, let's install it according to the official guide:

helm repo add jetstack https://charts.jetstack.io --force-update
helm install \
  cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.16.2 \
  --set crds.enabled=true

Install example service

Build and push example Docker image (optional)

TIP

If you don't want to build the Docker image by yourself, you can use my pre-built image kezhenxu94/nginx-ssl-example:1.0.0 directly.

We are going to use a very simple example service - an Nginx server with the default index page, here is the Dockerfile:

Dockerfile
FROM nginx:1.25.3

COPY nginx.conf /etc/nginx/nginx.conf

Let's build the Docker image and push it to a container registry:

docker buildx build --platform linux/amd64,linux/arm64 --push -t kezhenxu94/nginx-ssl-example:1.0.0 .

Deploy example service

In order to make it easier for you to modify the domain name and the image name, I'm going to use a Helm chart to deploy the example service, you can download the Helm Chart from this GitHub repository.

And here is the Helm chart values file:

values.yaml
image:
  repository: kezhenxu94/nginx-ssl-example:1.0.0
  pullPolicy: IfNotPresent
  tag: latest

replicas: 1
resources:
  cpu: 1
  memory: 500Mi

gateway:
  issuer:
    email: info@your-email.com
  hosts: &hosts
    - letsencrypt.kezhenxu94.me
  selector:
    istio: ingressgateway
  http:
    hosts: *hosts
  https:
    hosts: *hosts

in the gateway.hosts field, you should replace letsencrypt.kezhenxu94.me with your own domain name, and that's all you need to do.

Now let's install the example service with the Helm chart:

helm install letsencrypt-sample . --values ./values.yaml

After a few seconds, you should be able to see a Pod named cm-acme-http-resolver-xxxx, this is used for Let's Encrypt to verify your domain ownership:

kubectl get pods -A | grep acme
istio-system      cm-acme-http-solver-kckvx                             1/1     Running            0          11s

And the Pod will be deleted after your domain ownership is verified and the certificate is issued, you can check the certificate status by running:

kubectl get certificate -A

NAMESPACE      NAME                         READY   SECRET                       AGE
istio-system   default-letsencrypt-sample   True    default-letsencrypt-sample   4m30s

you can see the READY field is True, which means the certificate is ready to use.

Test the service

Now you can navigate to your domain https://letsencrypt.kezhenxu94.me in the browser, you should see the default Nginx index page, and the connection is secure with a valid certificate:

Nginx index page

If you click on the lock icon in the address bar, you can see the certificate information:

Certificate information

It's a valid Let's Encrypt certificate, and it will be auto-renewed by cert-manager before it expires, so you don't need to worry about it.

Like this post? Subscribe to stay updated and receive the latest post straight to your mailbox!