Securing your ingress with cert-manager & let's encrypt

In line with my previous article regarding external-dns component in a kubernetes cluster : https://yodamad.hashnode.dev/set-up-external-dns-on-a-kubernetes-cluster, here is a quick one explaining how to secure your nginx ingresses with a certificate to enable security in your environment.

This article is covering how to set up & configure the different elements to automatically generate a certificate provided by let's encrypt organism when deploying a new ingress in our cluster. Once it's done, every time an ingress will be deployed, with the right annotations, automatically a certificate will be generated to secure with TLS the HTTP endpoint.

Prerequisites for this article are :

  • A Kubernetes cluster running with an nginx ingress controller (and, optionally, external-dns) installed

  • kubectl installed on your laptop

  • helm installed, optionally

First, we have to install cert-manager component. This component is in charge to manage certificates in a cluster. A complete description is available here

# Install it with 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.11.0 \
  --set installCRDs=true

# or install it kubectl 
$ kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.11.0/cert-manager.yaml

We can control that everything goes well by checking that the new components related to cert-manager are known by the cluster

$ kubectl get crd | grep cert-manager
certificaterequests.cert-manager.io
certificates.cert-manager.io
challenges.acme.cert-manager.io
clusterissuers.cert-manager.io
issuers.cert-manager.io
orders.acme.cert-manager.io

$ kubectl explain Certificate
$ kubectl explain CertificateRequest
$ kubectl explain Challenge

Now, we need to create the component that will do the process to claim and generate a certificate regarding a certificate provider such as let's encrypt. This is done by creating a ClusterIssuer component. This will cover the whole cluster. NB: If you want to restrict this feature to a dedicated namespace, you can use an Issuer instead.

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-production
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: <your_email_to_receive_notifications>
    privateKeySecretRef:
      name: letsencrypt-production
    solvers:
      - http01:
          ingress:
            class: nginx

Here, each ingress will be detected and a certificate will be generated to secure them if TLS is configured. You can fine-grain the ingresses to handle using ingressTemplate solver approach describes in the official documentation. Now, let's modify the Ingress defined in the previous article:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hashnode
  annotations:
    kubernetes.io/ingress.class: nginx
    # Add this for detection
    # Replace cluster-issuer by issuer if you define an Issuer rather than a ClusterIssuer
    cert-manager.io/cluster-issuer: letsencrypt-production
spec:
  rules:
  - host: demo.hashnode.mydns.fr
    http:
      paths:
      - backend:
          service:
            name: hashnode
            port:
              number: 80
        pathType: Prefix
        path: /
  # Add this part to enable TLS
  tls:
    - hosts:
      - demo.hashnode.mydns.fr
      secretName: nginx-demo-tls
kubectl apply -f hashnode-demo.yaml -n hashnode

Once applied, we can see that

  • ingress controller is exposing 443 port

  • a certificate has been generated

$ kubectl get svc --all-namespaces | grep ingress-nginx-controller
ingress-nginx       ingress-nginx-controller             LoadBalancer   10.3.106.101   57.128.40.102   80:30542/TCP,443:30552/TCP   2d11h

$ kubectl get certificates -A
NAMESPACE   NAME             READY   SECRET           AGE
hashnode    nginx-demo-tls   True    nginx-demo-tls   81m

Now we can check that our website is secured with a valid certificate

And we can have a quick look at the certificate

So, in conclusion, with a few steps, we are now able to secure our endpoints for valid certificates generated by an official organism.

All sources are still available in my GitLab repository.

Did you find this article valuable?

Support Matthieu Vincent by becoming a sponsor. Any amount is appreciated!