SSL/TLS for Kubernetes with Cert-Manager and Let’s Encrypt

Akriotis Kyriakos
4 min readSep 2, 2022

--

Install cert-manager with Helm and deploy Let’s Encrypt Issuers.

Cert-manager is a cloud native certificate management solution for Kubernetes and OpenShift workloads. It obtains X.509 certificates from a broad spectrum of public or private Issuers and makes sure the certificates are valid and up-to-date. Otherwise it will attempt, behind the scenes, to automatically renew the certificates in a specified time-frame, so your workloads will not experience any operational disruption.

The key features of cert-manager are:

  • Automates the Certificates issuance and renewal in order to secure Ingresses with TLS
  • It supports Issuers from the most common public and private Certificate Authorities
  • Supports secure pod-to pod communication and Certificates for internal or external workloads

cert-manager adds Certificates and certificate Issuers as CRDs in Kubernetes, and takes completely over the process of obtaining, renewing and using those certificates. Supports various issuing sources as Let’s Encrypt, HashiCorp Vault but private PKIs as well.

Installation

Installing cert-manager is as simple as it gets, especially with Helm. First add the repository:

helm repo add jetstack https://charts.jetstack.io

update your local Helm chart repository cache:

helm repo update

and then install the chart:

helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.9.1 \
--set installCRDs=true

cert-manager requires the CRDs we mentioned above. They can be installed either manually using kubectl, or using the installCRDs clause with the chart installation. I go always for the latter.

Deploy Issuers & Cluster Issuers

The last thing we have to configure after cert-manager is successfully installed are Issuers or ClusterIssuers. These are resources that represent certificate authorities (CAs) able to sign certificates in response to certificate signing requests. The former are scoped only in the logical boundaries of a Namespace and the latter span the whole Cluster. Here’s how we can configure the issuers for Let’s Encrypt:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: cluster-issuer-letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: me@example.com
privateKeySecretRef:
name: cluster-letsencrypt-prod
solvers:
- http01:
ingress:
class: traefik
selector: {}

The ACME Issuer type represents a single account registered, using an email address, with the Automated Certificate Management Environment (ACME) Certificate Authority server. When you create a new ACME Issueror ClusterIssuer cert-manager will generate a private key, cluster-letsencrypt-prodin our example, that is used to identify you with the ACME server. Certificates issued by public ACME servers are typically trusted by client’s computers by default.

We have two type of solvers, a challenge has to be solved in order order for the ACME CA server to verify that a client owns the domain:

  • HTTP01 challenges are completed by presenting a computed key, that should be part of the URL endpoint and is routable over the internet.
  • DNS01 challenges are completed by providing a computed key that is present at a DNS TXT record. With the correct permissions, cert-manager will automatically present this TXT record for your given DNS provider.

The ingressClass value, in our example is traefik, depends on the Ingress you intend to use in order to expose your workloads (Traefik, HAProxy, nginx etc).

Let’s Encrypt provides two endpoints, one for production :

https://acme-v02.api.letsencrypt.org/directory

and one for staging:

https://acme-staging-v02.api.letsencrypt.org/directory

The production endpoint has a rate limiter, so it is wise to use always the staging one, while you are configuring your cluster or ingress till ensure that everything is working as expected and only then to revert to the production address.

An Issuer looks almost identical, except it specifies an additional metadata that is the namespace scope:

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: ns-default-issuer-letsencrypt-prod
namespace: default
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: me@example.com
privateKeySecretRef:
name: ns-default-letsencrypt-prod
solvers:
- http01:
ingress:
class: traefik
selector: {}

If you additionally need SelfSigned certificates you must deploy SelfSigned Issuers and ClusterIssuers:

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: cluster-issuer-selfsigned
spec:
selfSigned: {}

and

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
name: ns-default-issuer-selfsigned
namespace: default
spec:
selfSigned: {}

Save all these manifests to yaml files and create them with kubectl apply -f

Follow up

In an upcoming guide I will show you how to configure cert-manager to support various Ingresses, Traefik and HAProxy, and obtain Certificates for your domains and subdomains. Stay tuned!

--

--

Akriotis Kyriakos

talking about: kubernetes, golang, open telekom cloud, aws, openstack, sustainability, software carbon emissions