How to setup free ssl certificate in k8s using lets encrypt?

How to setup free ssl certificate in k8s using lets encrypt?

Learn how to install cert-manager using helm and use cert-manager to issue ssl certificates via Let’s Encrypt and configure SSL in traefik ingress.

In this article, we will learn to install cert-manager using helm and use cert-manager to issue ssl certificates via Let’s Encrypt and configure SSL in traefik ingress.

cert-manager

cert-manager is a certificate management solution for kubernetes. It helps in automating certificate management tasks like issuing new certificates and renew certificates. It creates ClusterIssuer and Certificate custom resource definitions in k8s which we can use to automate the certificate management process in a declarative manner.

It supports certificate issuers including,

  • Let’s Encrypt (ACME)
  • HashiCorp Vault
  • Venafi
  • private PKI

Certificate resolvers

Before issuing a certificate, The ACME CA server need to verify that a you own the domain to prevent a user fraudulently impersonate another’s domain. So the client needs to complete "challenges".

It provides two kinds of challenge mechanism.

  1. HTTP01
  2. DNS01

HTTP01 challenge

HTTP01 challenge completes domain ownership by uploading a computed key to the domain and verifies by accessing the computed key through HTTP.

For example, If you ask the cert-manager to issue a certificate for mywebsite.com, the issuer uploads some content at mywebsite.com/a-key.txt

If your domain is already accessible through http and you don't have access to DNS servers, you can use this method.

DNS01 challenge

DNS01 challenge completes domain ownership by adding a computed key to the DNS TXT record and verifies using DNS lookup.

Install cert-manager using helm

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

$ helm repo update

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

Verify cert-manager installation

Check cert-manager, cert-manager-cainjector, and cert-manager-webhook pods in the cert-manager namespace are in Running state.

$ kubectl get pods --namespace cert-manager

Create test-resources.yaml

cat <<EOF > test-resources.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: cert-manager-test
---
apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: test-selfsigned
  namespace: cert-manager-test
spec:
  selfSigned: {}
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: selfsigned-cert
  namespace: cert-manager-test
spec:
  dnsNames:
    - example.com
  secretName: selfsigned-cert-tls
  issuerRef:
    name: test-selfsigned
EOF

Apply the manifest to cluster

kubectl apply -f test-resources.yaml

Wait for few seconds and run below command to check if it say "Certificate issued successfully", then we are good to go.

kubectl describe certificate -n cert-manager-test

Create Lets Encrypt Staging Issuer

It is recommended to us staging issuer to verify the certificate issuer is setup correctly and to avoid rate limiting by lets encrypt. In this example, We are using http01 challenge method in to verify domain ownership.

create staging-issuer.yaml and set your ingress class.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
  namespace: myapp
spec:
  acme:
    # The ACME Staging server URL
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: myemail@myapp.com
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-staging
    # Enable the HTTP-01 challenge provider
    solvers:
      # An empty 'selector' means that this solver matches all domains
      - selector: {}
        http01:
          ingress:
            class: traefik/nginx/myingressclass

Create Certificate resource

create cert.yaml

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: my-app-com
  namespace: myapp
spec:
  secretName: myapp-com-tls
  issuerRef:
    name: letsencrypt-staging
    kind: ClusterIssuer
  commonName: myapp.com
  dnsNames:
    - www.myapp.com

Apply the manifest to the cluster

$ kubectl apply -f staging-issuer.yaml
$ kubectl apply -f cert.yaml

Verify certificate creation

Run this command below, and you should see "The certificate has been successfully issued" event. This process will create tls cerificate as a k8s secret with the name "myapp-com-tls". We can reference this secret name in ingress manifests to setup ingress.

$ kubectl describe certificate my-app-com

If not, copy resource id next to certicaterequest event and run the command below for example, if the message is "Created new CertificateRequest resource "selfsigned-cert-2gd9x", run

$ kubectl describe certificaterequest selfsigned-cert-2gd9x

You should see the reason for error.

Lets Encrypt Production Issuer

Copy the staging-issuer.yaml and. replace the server url to below

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

Configure traefik Ingress

Configure the ingress with the tls secret name mentioned in the cert.yaml

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: my-ingress
spec:
  entryPoints:
    - web
    - websecure
  routes:
    - match: Host(`myapp.com`)
      kind: Rule
      services:
        - name: my-app-svc
          port: 3000
  tls:
    secretName: myapp-com-tls

This will enable ssl for the myapp.com domain.

Thanks for reading. 😀

If you like this article, subscribe to the newsletter and Connect with me on twitter to get updates on my future articles. ✅

Did you find this article valuable?

Support Suresh Kumar by becoming a sponsor. Any amount is appreciated!