I’ll preface this blog post with the fact that I am FAR FAR away from any kind of expert on Kubernetes and AKS. I am still very early in my journey with it, and a lot of my experience so far is trial and error. I have also found that there is a lot of documentation out there on how to do things, but a lot of it assumes a fairly decent knowledge of how everything sticks together, so for a newbie to Kubernetes it can be quite challenging.

So recently while working on some solutions at work, I had a need to test out some HTTPS ingress into an Azure Kubernetes (AKS) instance. The most logical and easiest way to do this is with the Azure Application Gateway Ingress Controller (AGIC). Honestly this is an amazing service from Azure and makes everything super easy, and for production instances I would highly recommend this. But for this project, the ingress being used was Traefik and the requirement was to use automatically generated certificates from Lets Encrypt.

This is a fully support solution on Traefik, so once I spent a few hours reading, along with trial and error, it was actually pretty simple. So I have tried to condense the steps down here to what I did. to get it working.

Note - All the files I use for this are available in my GitHub Repo if you want to download and use instead of copy/paste.

Setup an AKS Cluster

You could use any deployment of Kubernetes for this, AKS, EKS, GKE or Minikube. They should all work, but for me I’m using Azure so this is based around AKS. So first of all lets setup a quick single node AKS Cluster, which I’m going to call Ironman.

# Login in Azure
az login
# Create some variables
rsg="aks-traefik"
location="australiaeast"
aksname="ironman"

# Create a resource group
az group create --name $rsg --location $location

# Create an AKS cluster
az aks create --resource-group $rsg --name $aksname --node-count 1 --enable-addons monitoring --generate-ssh-keys

# Get the AKS credentials to use in my local kubeconfig
az aks get-credentials --resource-group $rsg --name $aksname

Install Traefik

Now your cluster is setup and working we need to install Traefik. By far the easiest way to do this is with the Helm Chart that Traefik provide. I am assuming here you have Helm setup but if you haven’t there are excellent steps here for installing Helm.

# Add the Traefik Repo
helm repo add traefik https://helm.traefik.io/traefik
# Update Helm
helm repo update
# Install Traefik to your cluster
helm install traefik traefik/traefik

As long as that all goes through you should have Traefik installed in your cluster, which for me using K9S to view showed it all ready to go. traefik_running

Install a Demo App

Obviously you need somewhere to send traffic to for this demo. You can use anything, but for quick web frontend demos I am a big fan of the Containous Whoami docker image. Its nice and lightweight and gives a lot of good info when you are testing web traffic flows.

To deploy it create a yaml file using whatever editor you use (its VSCode right?) with the below. As mentioned earlier, you can also find all these files in my GitHub repo.

Save the file as 001-app.yaml

---
kind: Deployment
apiVersion: apps/v1
metadata:
  namespace: default
  name: whoami
  labels: 
    app: whoami
spec:
  replicas: 1
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
      - name: whoami-container
        image: containous/whoami
        ports: 
          - name: web
            containerPort: 80

Once you have done that deploy it to your cluster

kubectl apply -f 001-app.yaml

Service for the App

Next step is to create a simple service for the App to route traffic inside of the cluster.

Save this file as 002-service.yaml.

---
kind: Service
apiVersion: v1
metadata:
  name: whoami

spec:
  type: ClusterIP
  ports:
  - protocol: TCP
    port: 80
    name: web
  selector:
    app: whoami

And then deploy it

kubectl apply -f 002-service.yaml

External DNS Configuration

At this stage we have Traefik installed but not doing anything, as well as a demo app and local service. Next you need to point an external DNS name to your cluster ingress. This will depend entirely on your DNS provider, but for me I use Cloudflare with a cheap .xyz domain for test. Run the below command and take the external-ip of your traefik ingress and use that IP for your DNS name.

kubectl get service

Setup Certificate Manager

To generate Lets Encrypt Certificates we need to use a Certificate Manager and the one I saw come up the most and had the most easy to understand documentation was Cert Manager.

To install it execute the following which is the latest stable version at the date of blog.

kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.3.1/cert-manager.yaml

This installs pretty quick and you can see when its ready as a few pods will show up like this.

cert_manager

Setup Lets Encrypt

Now we get to the meaty part of the cert creation. There are a couple of steps to this, the first being to configure the cluster issuer to use Let Encrypt. You can use the below manifest file, just change your email address and name of the secret to something you want to use. Save the file as 003-lets-encrypt.yaml.

---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: whoami-cert # CHANGE THIS NAME
  namespace: default
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: changem@gmail.com # CHANGE TO YOUR EMAIL
    privateKeySecretRef:
      name: whoami-cert # CHANGE THIS NAME TO MATCH THE METADATA
    solvers:
    - http01:
        ingress:
          class: traefik

Then apply it

kubectl apply -f 003-lets-encrypt.yaml

Next you will do the actual certificate creation part. You will reference the issuer name from the previous step, but the other parts you need are the public DNS name of what you need the certificate to work with. Again you can use the below manifest for this part, call it 004-lets-encrypt-cert.yaml and change the settings to reflect your domain and names.

---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: aks.mydomain.xyz # This should be your domain name
  namespace: default
spec:
  secretName: aks.mydomain.xyz-tls # This will be your cert name in Kubernetes
  issuerRef:
    name: whoami-cert # This should match from 003-lets-encrypt.yaml
    kind: ClusterIssuer
  commonName: aks.mydomain.xyz # This is the main DNS name for the cert
  dnsNames:
  - aks.mydomain.xyz # You can add subdomains if you wish

Then apply it

kubectl apply -f 004-lets-encrypt-cert.yaml

Once that is done it can take a few minutes to create the cert. If you run kubectl get certificate you can see it being created, just wait for it to changed to TRUE as a Ready State. In my tests it takes about 2-3 minutes to fully come up. If its longer than that you have probably made a mistake in the yaml, so running kubectl describe certificate will show you the logs of whats happening. Mine looked like this when it was ready.

cert_ready

Ingress Route to the App

Final step, lets tell Traefik to find your app. This is done using an IngressRoute definition, and as long as your certificate has created OK, your DNS is pointed to your Traefik public IP, and you change all the values to reflect your certificate name and DNS names, the below manifest should do the trick. Save it as 005-ingress-route-https.yaml.

---
kind: IngressRoute
apiVersion: traefik.containo.us/v1alpha1
metadata:
  name: whoami
  namespace: default
  annotations: # If you are using local certs and not lets Encrypt - Remove this
    kubernetes.io/ingress.class: traefik  # If you are using local certs and not lets Encrypt - Remove this
    cert-manager.io/cluster-issuer: whoami-cert  # If you are using local certs and not lets Encrypt - Remove this

spec:
  entryPoints: 
    - websecure
  tls:
    secretName: aks.mydomain.xyz-tls # Lets Encrypt Generated Cert from 004-lets-encrypt-cert.yaml - Change this to local cert if you don't want to use Lets Encrypt.
    domains:
    - main: aks.mydomain.xyz
      sans:
      - aks.mydomain.xyz

  routes:
  - match: Host(`aks.mydomain.xyz`) 
    kind: Rule
    services:
    - name: whoami
      port: 80

And apply the ingressroute manifest.

kubectl apply -f 005-ingress-route-https.yaml

Fingers Crossed…..

Time to see if this worked. Open a browser and go to your domain name you setup earlier, mine for this test was https://ironman.andyroberts.xyz (won’t work for you), and see if the app loads….

The SSL cert should be valid, include your domain name and be issued by R3 which is Lets Encrypt.

site_done

Congratulations!!!! You did it, a nice simple HTTPs ingress to AKS via Traefik with auto generated SSL certificates.

Now I wouldn’t use this in production personally, I normally prefer full purchased certificates as there are a myriad of reasons to do that, but that all depends on your individual needs. For many this could be all you need and I hope this has been helpful.

Bonus - Using a full purchased SSL Cert

Like I said just, you may not want to use a dynamic Lets Encrypt cert. If you already have a full SSL cert, probably a wildcard for your site, its actually super easy to do this. I don’t know if this is production ready but it was fine for me in quick testing so if its not please let me know.

Take your existing cert and upload it into Kubernetes as a secret

kubectl create secret tls mysecretname --key mycert.key --cert mycert.crt

Then in the 005-ingress-route-https.yaml change the tls secretName to be the name of your new local secret, which above is mysecret.

Hope that helps! Have fun!

Andy