Auto provisioning of Letsencrypt TLS certificates for Kubernetes services deployed to an AKS cluster using cert-manager and nginx-ingress controller

Maninderjit (Mani) Bindra
4 min readMay 22, 2018

--

This post is linked from my first post in this series.

The rest of this post assumes that the AKS Kubernetes cluster is available, you have helm installed, and we have already executed the helm init command.

Create or update the nginx-ingress controller

The first thing we do now is install the inginx-ingress controller using helm. The github page for the nginx-ingress controller helm chart is at nginx-ingress. The install command to be used is :

Ingress controller install command

$ helm install stable/nginx-ingress

We will now create a sample deployment and service for which we can enable TLS.

Create the hello world web server deployment and service for which we can later enable TLS

Create Deployment

$ kubectl run web --image=tutum/hello-world --port=80

Expose the deployment as a service

$ kubectl expose deployment web --port=80

Check if deployment service and pods are up

$ kubectl get deployment web
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
web 1 1 1 1 3h
$ kubectl get svc web
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
web 10.3.17.199 <none> 80/TCP 3h
$ kubectl get pod -l=run=web
NAME READY STATUS RESTARTS AGE
web-5bff8ffd8c-tkfnq 1/1 Running 0 3h

After this my domain’s (manitestdomain.com) dns configurations was modified so that the domain manitestdomain.com was pointing to the public ip of the nginx-ingress controller service. This is the domain for whose subdomain (www.manitestdomain.com) I wanted to provision the Letsencrypt TLS certificate for.

Next we Install cert-manager to the kubernetes cluster using the helm chart.

You can find the cert-manager getting started guide here

Install cert-manager

$ helm install --name cert-manager --namespace kube-system --set rbac.create=false stable/cert-manager --set ingressShim.extraArgs='{--default-issuer-name=letsencrypt-prod,--default-issuer-kind=ClusterIssuer}'

Installation of cert-manager helm chart creates 3 new Custom resource definitions (CRDs) in kubernetes cluster, the Issuer CRD, the ClusterIssuer CRD and the Certificate CRD. The key point to note from the above command is the ingressShim.extraArgs, by default ingress shim is enabled, and it is the ingress shim which makes automated provisioning of TLS certificates based on ingress annotations possible. Here the ingress shim arguments tell the ingress shimthe default Issuer name and the kind of issuer (Issuer or ClusterIssuer). The cert-manager getting started guide provides more details about these setting. More details of the ingress shim settings can be found here . You will also notice that in this sample command I have turned rbac.create=false. You can set this to true if you have RBAC configured for your Kubernetes cluster.

Next we need to create a ClusterIssuer resource with the same name letsencrypt-prod as we have mentioned in the ingressShim.extraArgs .

Create the Cluster Issuer Resource

The cluster resource yaml file is

apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# The ACME server URL
server: https://acme-v01.api.letsencrypt.org/directory
# Email address used for ACME registration
email: user@manitestdomain.com
# Name of a secret used to store the ACME account private key
privateKeySecretRef:
name: letsencrypt-prod
# Enable the HTTP-01 challenge provider
http01: {}

The kind of Issuer and the name of the Issuer should match the values mentioned in the ingressShim.extraArgs (of the cert-manager helm installation command). cert-manager uses the ACME protocol to verify ownership of the domain before getting certificates from Letsencrypt. In the yaml above we have provided the url of the production Letsencrypt ACME server. The staging url of the Letsencrypt ACME server is https://acme-staging.api.letsencrypt.org/directory . You can find more detail on these settings at the link

Apply the above yaml

$ kubectl apply -f kubectl apply -f cluster-issuer-prod.yaml

At this point the cert-manager ingress shim is monitoring ingress rules, and based on the certain annotations it will fetch the certificates from Letsencrypt. Now we can create the ingress rule.

Create Ingress rule to configure TLS for the service.

The ingress yaml is

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: www-ingress
annotations:
kubernetes.io/ingress.class: nginx
kubernetes.io/tls-acme: 'true'
spec:
rules:
- host: www.manitestdomain.com
http:
paths:
- path: /
backend:
serviceName: web
servicePort: 80
tls:
- hosts:
- "www.manitestdomain.com"
secretName: manitestdomain-tls

The annotation kubernetes.io/tls-acme: ‘true’ tells the cert-manger ingress shim to fetch the letsencrypt certificate for the host domain “www.manitestdomain.com” and save it as the secret manitestdomain-tls .

Apply the ingress rule

$ kubectl apply -f www-ingress.yaml

Once cert-manager fetches the certificate for www.manitestdomain.com we will see that a new secret manitestdomain-tls gets created.

$ kubectl describe secret manitestdomain-tls
Name: manitestdomain-tls
Namespace: default
Labels: <none>
Annotations: <none>
Type: kubernetes.io/tlsData
====
tls.crt: 3822 bytes
tls.key: 1675 bytes

At this point we have TLS configured for our domain.

Check the certificate information from the Browser

Certificate Info in Browser
access to www.manitestdomain.com from Browser over https successful

--

--

Maninderjit (Mani) Bindra

Gopher, Cloud, Containers, K8s, DevOps | LFCS | CKA | CKS | Principal Software Engineer @ Microsoft