I’ve got a few readers/viewers asking me to make some content about using Traefik or Kong as Ingress controller. There is no doubt that Kubernetes is one of the hottest topic in the past few years. Normally the more powerful a tool is, the harder it is to be used. I am always reluctant to write a post that is either tool long or difficult to follow. However, deploying applications with helm on Kubernetes is actually quite easy. In today’s post, I would like to show you how to use cert manager to fulfil your TLS needs on Kubernetes.
Cert manager is a very straight forward tool. Users only need two steps to get certificate.
- Define an issuer for issuing certificates.
- Request certificate from this issuer.
Moreover because certificates are stored in secrets, you can use it with most if not all Kubernetes applications.
(Optional) : Loadbalancer, I am using MetalLB.
We use below command to install cert manager, it creates namespace
cert-manager, install CRDs and set nameservers to
188.8.131.52:53\,184.108.40.206:53 for DNS01 validation.
In case you don’t know,
220.127.116.11is Google’s DNS server and
18.104.22.168is Cloudflare’s. Both DNS servers are arguably the fastest right now.
helm install \
Let’s wait for all deployment complete.
kubectl get deploy -o name -n cert-manager | xargs -n1 -t kubectl rollout status -n cert-manager
cert-manager supports 6 issuer types. I will cover 3 types that I have used before.
This issuer as its name suggested issues certificate for itself. This is the quickly way to get a TLS certificate and start encrypting data transmission between your applications. You can define self-signed issuer as below and then request your certificate with whatever information you need, the certificate will be generated immediately.
Of courses there are drawbacks of using self-signed certificates mainly because it is issued on the fly so it won’t be trusted if there are any checks in place. To find out more information and how to overcome some of issues, please check official doc.
We often see organisations having their internal PKI set up and all their devices will trust certificates issued by internal CA automatically. In order to get certificate issued by internal CA (sometimes it is a requirement), users can create a CA issuer and all certificates issued by this issuer will be under company’s internal root.
To create a CA issuer, we need to have CA key pair.
Create Secret with ca key pair.
Because I plan to create a cluster issuer, I am creating this secret in
kubectl create secret tls -n cert-manager ca-key-pair --cert=ca.cert.pem --key=ca.key.pem
You can see this issuer is using the key pair we just created as CA.
Similar to self-signed issuer, all certificate requests to CA issuer will be fulfilled automatically. Unlike self-signed, these certificate will be issued under a certain root which means they will be valid if root is trusted on your devices.
This is the most important issuer type which use ACME protocol to request valid SSL certificates from CAs. Most users will only use this issuer type. I will divide the configuration into three parts.
Users define basic information of this issuer.
- email: Please use genuine email here because CA won’t allow bogus email and they will use this email to contact you about expiring certificate.
- server: ACME directory URL.
- privateKeySecretRef: This is the secret name that stores account’s private key.
- (Optional) disableAccountKeyGeneration: If you have your own account keys you want to reuse, please set this to
- (Optional) External Account Binding: Most commercial CAs tend to use external account binding with their ACME servers. If the CA you are using (Sectigo, ZeroSSL, Digicert etc.) requires it, please follow official doc to add it here
For a list of free CA ACME directory URLs, please check my previous post.
This validation requires port 80 to be opened on the server as per RFC8555. If you can’t have this port opened, you can’t use this method. When user request a certificate from issuer using http01 validation method, an ingress object will be created automatically. The setting below are related to what’s on the ingress object.
- class: This adds
kubernetes.io/ingress.class:annotation on the ingress object. Some ingress controllers only need this annotation to be able to detect and satisfy the ingress object.
- (Optional) ingressTemplate: If you need to add more annotations or labels to the ingress object, you can add it as below.
The ingress object generated from above config looks like below (I was created on an ingress object):
As soon as the validation completed (which means this host name and path is reachable), certificate will be issued and stored in the secret name we defined on our certificate or ingress object.
To know more settings about HTTP01 validation, please check official doc.
DNS validation is the method I alway use. In my opinion there are two main reasons to use this method:
Users don’t need to worry about firewall, Loadbalancer, proxies etc which could potentially block http validation.
Users can request wildcard certificate with DNS01 validation.
However, users need privilege to access domain DNS and not all DNS providers are supported at the moment.
Let me use
Cloudflare as an example.
First you need to get an
API tokenfrom Cloudflare. If you aren to sure, please follow this Cloudflare doc. Once we’ve got our token, we need to create secret to store it. As I am creating a
ClusterIssuer, I need to put this token in
kubectl create secret generic cloudflare-api-token-secret -n cert-manager --from-literal=api-token=<CLOUDFLARE_API_TOKEN>
Reference api token on dns01 resolver.
Combine the general info section with DNS section, we’ve got our issuer as below.
I hope you will know what type of issuer you will use and how to define an issuer after reading above. We will start requesting certificates in the next part.
There are two main ways of requesting a certificate. The first method is to create a certificate object which gets the certificate and stores them in a secret first and then users can use this secret on their application or other Kubernetes resources ingress for example.
The other method is to create ingress objects with cert-manager annotations. Certificate object will be created automatically based on the info on the ingress. Let me show you how these two methods work.
Personally this is the method I will go for because I am skeptical and need to make sure the certificate exists before using it. There are just too many reasons a certificate request can fail.
You can find an example of certificate object below and I’ve put my comments between lines. You can find the list of certificate specs from here. I only put down whatever I think is useful for my use case.
For users that only use Trusted CAs (mostly
Let's Encrypt) to issue their certificate, you don’t need to stress too much about certificate settings. Only CA can decide what’s on the certificate they issue anyway. You can use below to get a RSA 2048 three month validity certificate that secures
li.test. This certificate will be renewed 30 days before expiry date automatically.
If you want to know more about how certificate object request certificate from CA servers, please check official doc, it explains very well.
This really is just a standard
ingress resource. The only thing I added is a
cert-manager.io/cluster-issuer annotation which tells cert manager to create a certificate for hosts name
echo.liy.id.au and store certificate in secret called
cm-demo-cert-ingress-secret. Once this ingress object is created, cert-manager generates the certificate objects and request certificate from lets encrypt automatically. The traffic goes to your route will start using valid certificate once the validation is completed.
If you want to know more about cert-manager annotations, please check official doc.
That’s all I want to cover today, I hope it is useful to you.