Integrating LetsEncrypt with Azure Kubernetes Service (AKS) for free TLS certificates
Table of Contents
Let’s Encrypt is a free alternative to traditional certificate authorities which also takes care of the certificate renewal process for you. This tutorial goes through the steps of deploying and configuring a new Azure Kubernetes (AKS) cluster and then use the cert-manager resource to automatically generate and renew TLS certificates using Let’s Encrypt.
You will set up the new AKS cluster, deploying the Azure DNS resources, and configuring cert-manager
, all using a few interactive bash scripts.
Prerequisites
- An Azure account with an active subscription. If you don’t have one, you can create a free account here.
- The Azure CLI installed on your local machine. You can find the installation instructions here.
- The
kubectl
command-line tool installed on your local machine. You can find the installation instructions here. jq
installed on your local machine for parsing JSON content; install it from here.- A domain name that you own and can manage the DNS records for; we will use this domain to generate the TLS certificate.
If you want to jump ahead and get the source code, it's on Github at https://github.com/pineviewlabs/Azure-samples/tree/main/letsencrypt-azure-kubernetes
Step 1 — Setup the Azure Kubernetes Service (AKS) Cluster
First, we need to create a new AKS cluster. We will use the Azure CLI to create the cluster, so make sure you have it installed on your local machine.
Login to your Azure account using the Azure CLI:
az login
If you have more than one subscription, set the default subscription to the one you want to use for this tutorial:
az account set --subscription <subscription_id>
Create a new resource group for the AKS cluster. I'm using westeurope
as the location for the resource group, but you can choose a different location if you prefer. For the resource group name, I'll use a convention which includes the region name so that it's easier to identify the region where the resources are deployed:
az group create --name we1-akstutorial-rg --location westeurope
Now, let's create the AKS cluster. I've put together an interactive bash script which will guide you through the process of creating the AKS cluster.
Overview of the process
The script will ask you for:
- Resource group name – can be a new or existing resource group (if it doesn't exist, it will be created)
- AKS cluster name
- Kubernetes node count (i.e. how many VMs to use)
- Node size (i.e. the VM size to use) -- see the list of available VM sizes; by default we'll use
Standard_D2s_v3
- Whether you want to generate SSH keys for the cluster.
Running the script
To run the script, copy and paste the following command in your terminal (assuming you have curl
installed):
bash <(curl -sL https://raw.githubusercontent.com/pineviewlabs/Azure-samples/main/letsencrypt-azure-kubernetes/01-create-aks-cluster.sh)
You can also clone the repository and run the script from your local machine:
git clone https://github.com/pineviewlabs/Azure-samples.git
cd letsencrypt-azure-kubernetes
chmod +x 01-create-aks-cluster.sh
./01-create-aks-cluster.sh
After the script completes,you should have a new AKS cluster running in your Azure subscription.
Example output
$ ./01-create-aks-cluster.sh
Enter Azure Subscription ID [xxxxxxxx-xxxx-xxxx-xxxx-xxxx]:
✔️ Using Azure Subscription ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxx
Enter Azure resource group name [we1-akstutorial-rg]:
ℹ️ Checking if resource group 'we1-akstutorial-rg' exists...
true
✔️ Resource group 'we1-akstutorial-rg' already exists. Using existing group.
Use existing SSH key found at /home/tutorials-user/.ssh/id_rsa.pub? [Y/n]: Y
✔️ Using existing SSH key.
Enter AKS cluster name [we1-akstutorial-cluster]:
Enter node count [1]: 2
Enter node VM size [Standard_D2s_v3]:
ℹ️ Creating AKS cluster 'we1-akstutorial-cluster' in resource group 'we1-akstutorial-rg'...
_| Running...
Merged "we1-akstutorial-cluster" as current context in /Users/<your-user>/.kube/linux-config
✔️ AKS cluster created.
Verify that you can connect to the AKS cluster by running the following command:
kubectl get nodes
Congratulations! We're now done with the Kubernetes cluster setup.
Step 2 — Deploy cert-manager and ClusterIssuer resources
Now that we have the AKS cluster set up, the Azure DNS resources created, and the DNS records updated, we can proceed to deploy the cert-manager
resource to the AKS cluster.
Overview of the process
For this step of the process, we'll run an interactive bash script which will do the following:
- after the preliminary checks, the script will prompt you for an Azure Service Principal name
- by default it iscert-manager-dnssp
in the Azure AD tenant associated with the subscription;
- this service principal will be used bycert-manager
to authenticate with Azure DNS and manage the DNS records for the domain;
- if the service principal already exists, the script will create a new client secret for it. - create a Kubernetes secret named
azuredns-config
, in a separeatecert-manager
namespace, which will store the Azure Service Principal credentials.
- this is required bycert-manager
to authenticate with Azure DNS and validate the domain ownership. - deploy the
cert-manager
resources to the AKS cluster. - load the
cluster-issuer-template.yaml
template file and replace the placeholders with the actual values provided by the user. - run the
kubectl apply
command to deploy thecert-manager
andClusterIssuer
resources to the AKS cluster.
Running the script
To run the script,open a new terminal and run the following command:
bash <(curl -sL https://raw.githubusercontent.com/pineviewlabs/Azure-samples/main/letsencrypt-azure-kubernetes/02-install-cert-manager.sh)
Or clone the repository and run the script from your local machine:
git clone https://github.com/pineviewlabs/Azure-samples.git
cd letsencrypt-azure-kubernetes
chmod +x 02-install-cert-manager.sh
./02-install-cert-manager.sh
Example output
$ ./02-install-cert-manager.sh
Enter Azure Subscription ID [xxxxxxxx-xxxx-xxxx-xxxx-xxxx]:
✔️ Using Azure Subscription ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxx
Enter Azure resource group name [we1-akstutorial-rg]:
Enter the email address to use with Let's Encrypt: <your-email-address>
Enter your domain name [letsencrypt-aks-tutorial.yourdomain.dev]: letsencrypt-aks-tutorial.pineview.io
ℹ️ Checking for existing service principal named 'cert-manager-dnssp'...
ℹ️ Service Principal already exists with ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxx
ℹ️ Tenant ID retrieved: xxxxxxxx-xxxx-xxxx-xxxx-xxxx
ℹ️ Creating a new client secret for Service Principal ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxx...
WARNING: The output includes credentials that you must protect. Be sure that you do not include these credentials in your code or check the credentials into your source control. For more information, see https://aka.ms/azadsp-cli
ℹ️ New client secret created successfully.
ℹ️ Creating Kubernetes secret...
ℹ️ Namespace 'cert-manager' already exists.
ℹ️ Installing or updating cert-manager to version v1.14.5...
# Lots of output from kubectl ...
✔️ ClusterIssuer configuration has been applied.
Error from server (InternalError): error when creating "STDIN": Internal error occurred: failed calling webhook "webhook.cert-manager.io":...
, that means that the cert-manager resources are not yet ready. You can just re-run the script in a minute or two and it should work.Step 3 — Deploy and Nginx Ingress and setup the Azure DNS resources
Now that we have the cert-manager
and ClusterIssuer
resources deployed, we can proceed to deploy the TLS
ingress resource to the AKS cluster.
This is the final step in the process and will configure the cert-manager
to automatically generate and renew TLS certificates for the domain we specified.
Next, assuming you already have a registred domain name, we need to deploy the Azure DNS resources that will be used to manage the DNS records for the domain we want to use for the TLS certificates.
Overview of the process
The interactive bash script in this section will attempt to do the following:
- deploy the
nginx-ingress
controller to the AKS cluster - deploy a static website using the
dockersamples/static-site
image setup the service - configure an ingress resources to route traffic to the static website
- create an Azure DNS zone and an A record for the domain name
Issuer Configuration
DNS01
challange provider for the cert-manager Issuer resource,which means that the cert-manager will create a new DNS TXT
record for the domain and Let's Encrypt will verify the domain ownership by checking the TXT record. You can also use the HTTP01 challange provider, however, for this tutorial we have opted for the DNS01 challenge provider which integrates better with Azure DNS. Read more about the challenge providers here.
Running the script
To run the script, open a new terminal and run following command:
bash <(curl -sL https://raw.githubusercontent.com/pineviewlabs/Azure-samples/main/letsencrypt-azure-kubernetes/03-deploy-ingress.sh)
Or clone the repository and run the script from your local machine:
git clone https://github.com/pineviewlabs/Azure-samples.git
cd letsencrypt-azure-kubernetes
chmod +x 03-deploy-ingress.sh
./03-deploy-ingress.sh
Example output
$ ./03-deploy-ingress.sh
Enter the domain name for the DNS zone [letsencrypt-aks-tutorial.yourdomain.dev]:
Enter the IPv4 address for the DNS A record [xxx.xxx.xxx.xxx]:
ℹ️ Creating DNS zone 'letsencrypt-aks-tutorial.pineview.io' in resource group 'we1-kubernetes-dev'...
{
"etag": "...",
"id": "/subscriptions/.../resourceGroups/we1-kubernetes-dev/providers/Microsoft.Network/dnszones/letsencrypt-aks-tutorial.pineview.io",
"location": "global",
"maxNumberOfRecordSets": 10000,
"name": "letsencrypt-aks-tutorial.pineview.io",
"nameServers": [
"ns1-35.azure-dns.com.",
"ns2-35.azure-dns.net.",
"ns3-35.azure-dns.org.",
"ns4-35.azure-dns.info."
],
"numberOfRecordSets": 2,
"resourceGroup": "we1-kubernetes-dev",
"tags": {},
"type": "Microsoft.Network/dnszones",
"zoneType": "Public"
}
✔️ DNS zone created.
ℹ️ Adding A record with IP 'xxx.xxx.xxx.xxx' to zone 'letsencrypt-aks-tutorial.pineview.io'...
{
"ARecords": [
{
"ipv4Address": "xxx.xxx.xxx.xxx"
}
],
"TTL": 3600,
"etag": "...",
"fqdn": "letsencrypt-aks-tutorial.pineview.io.",
"id": "/subscriptions/.../resourceGroups/we1-kubernetes-dev/providers/Microsoft.Network/dnszones/letsencrypt-aks-tutorial.pineview.io/A/@",
"name": "@",
"provisioningState": "Succeeded",
"resourceGroup": "we1-kubernetes-dev",
"targetResource": {},
"trafficManagementProfile": {},
"type": "Microsoft.Network/dnszones/A"
}
✔️ Done. Ingress Controller has been deployed and DNS zone has been updated.
ℹ️ Add a new NS record for 'letsencrypt-aks-tutorial.pineview.io' with these values in your registrar:
ns1-35.azure-dns.com
ns2-35.azure-dns.net
ns3-35.azure-dns.org
ns4-35.azure-dns.info
Error from server (InternalError): error when creating "STDIN": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io":...
,that means that the Nginx Ingress resources are not yet ready.You can just re-run the script in a minute or two.Verifying the certificate status
To check the certificate status, you can run the following command:
kubectl get certificaterequests
If the certificate is not ready,you can describe the requests to get more information:
kubectl describe certificaterequests
Troubleshooting
If you are still waiting on the certificate to be issues for longer than a few minutes,check the logs of the cert-manager pod that handles the certificate request:
kubectl logs -n cert-manager $(kubectl get pods -n cert-manager -l app=cert-manager -o jsonpath='{.items[0].metadata.name}')
In more complex DNS setups,the certificate request might get stuck.In that case,you can try to delete the certificate request pod and let cert-manager recreate it:
kubectl delete pod -n cert-manager $(kubectl get pods -n cert-manager -l app=cert-manager -o jsonpath='{.items[0].metadata.name}')
Update the domain's DNS records
After running the script, you will get the name servers for the newly created DNS zone. You need to update the domain's DNS records in your domain registrar's control panel to point to these name servers. This is usually done by updating the NS records for the domain and unfortunately it is a manual process.
If you are using Azure DNS as your domain registrar, you can follow the instructions here to delegate the domain to Azure DNS.
Otherwise, go into your domain registrar's control panel and add a new NS
record for the domain (in this example we used letsencrypt-aks-tutorial.pineview.io
) and the NS records were provided in the output of the script:
ns1-35.azure-dns.com.
ns2-35.azure-dns.net.
ns3-35.azure-dns.org.
ns4-35.azure-dns.info.
Conclusion
Deploying and configuring TLS certificates for your Kubernetes workloads can be a complex process, evidently. I went ahead and created the interactive bash scripts presented in this tutorial to automate most of the process, which is something we often have to do for our customers here at Pineview.
There's more information available in the official guide for AKS from the cert-manager at https://cert-manager.io/docs/tutorials/getting-started-aks-letsencrypt.
You can find the complete source code on Github: https://github.com/pineviewlabs/Azure-samples/
You reach out to us via the contact form on our hompage here: pineview.io, via Twitter at @pineviewlabs or find us on LinkedIn at Pineview.