Secure your AKS Ingress with LetsEncrypt and Cert-Manager

This article shows how to secure the deployment of the NGINX ingress controller in an Azure Kubernetes Service (AKS) cluster. The ingress controller is configured with a static public IP address on Azure Standard Load Balancer. The cert-manager project is used to automatically generate and configure Let’s Encrypt certificates. A custom domain will be integrated with a certificate to run the application publicly.

Actions to be performed:

  • Create a namespace ingress-basic for Ingress Controller where all ingress controller-related resources will be created.
  • Create a DNS Zone and Alias record for the custom domain.
  • Install cert-manager for SSL certificates in ingress-basic namespace using Helm.
  • Create a CA cluster issuer for issuing certificates.
  • Create first application and service.
  • Create Second application and service.
  • Create an ingress route to configure the rules that route traffic to one of the two applications.
  • Verify the automatic created certificate.
  • Test the applications using Custom Domain.

Architecture

Architecture securing web app with cert-manager and let`s encrypt : Image reference : https://medium.com/geekculture/aks-with-cert-manager-f24786e87b20

Create an ingress controller

# Create a namespace for ingress resources
kubectl create namespace ingress-basic


# Add the Helm repository
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
# Use Helm to deploy an NGINX ingress controller
helm install ingress-nginx ingress-nginx/ingress-nginx \
    --namespace ingress-basic \
    --set controller.replicaCount=2

Create DNS Zone and A record

A record of custom domain will be the Public IP of Ingress Controller.

Install cert-manager

Label the cert-manager namespace to disable resource validation

kubectl label namespace ingress-basic cert-manager.io/disable-validation=true# Add the Jetstack Helm repository

Update your local Helm chart repository cache

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

Install CRDs with kubectl

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.7.1/cert-manager.crds.yaml

Install Cert-Manager with Helm Chart

cert-manager Helm chart
helm install cert-manager jetstack/cert-manager \
  --namespace ingress-basic \
  --version v1.7.1

Create a CA cluster issuer

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: youreemail@gmail.com
    privateKeySecretRef:
      name: letsencrypt
    solvers:
    - http01:
        ingress:
          class: nginx
          podTemplate:
            spec:
              nodeSelector:
                "kubernetes.io/os": linux

To create the issuer, use the kubectl command.kubectl apply -f cluster-issuer.yaml –namespace ingress-basic

Run demo applications

Create a aks-helloworld-one.yaml file and copy in the following example YAML:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: aks-helloworld-one
spec:
  replicas: 1
  selector:
    matchLabels:
      app: aks-helloworld-one
  template:
    metadata:
      labels:
        app: aks-helloworld-one
    spec:
      containers:
      - name: aks-helloworld-one
        image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
        ports:
        - containerPort: 80
        env:
        - name: TITLE
          value: "Welcome to Azure Kubernetes Service (AKS)"
---
apiVersion: v1
kind: Service
metadata:
  name: aks-helloworld-one
spec:
  type: ClusterIP
  ports:
  - port: 80
  selector:
    app: aks-helloworld-one

Create a aks-helloworld-two.yaml file and copy in the following example YAML:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: aks-helloworld-two
spec:
  replicas: 1
  selector:
    matchLabels:
      app: aks-helloworld-two
  template:
    metadata:
      labels:
        app: aks-helloworld-two
    spec:
      containers:
      - name: aks-helloworld-two
        image: mcr.microsoft.com/azuredocs/aks-helloworld:v1
        ports:
        - containerPort: 80
        env:
        - name: TITLE
          value: "AKS Ingress Demo"
---
apiVersion: v1
kind: Service
metadata:
  name: aks-helloworld-two
spec:
  type: ClusterIP
  ports:
  - port: 80
  selector:
    app: aks-helloworld-two

Run the two demo applications using kubectl:

kubectl apply -f aks-helloworld-one.yaml --namespace ingress-basic
kubectl apply -f aks-helloworld-two.yaml --namespace ingress-basic

Create an ingress route

The ingress resource configures the rules that route traffic to one of the two applications.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-world-ingress
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt
    nginx.ingress.kubernetes.io/rewrite-target: /$1
    nginx.ingress.kubernetes.io/use-regex: "true"
spec:
  tls:
  - hosts:
    - mydevsecops.com
    secretName: tls-secret
  rules:
  - host: mydevsecops.com
    http:
      paths:
      - path: /hello-world-one(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: aks-helloworld-one
            port:
              number: 80
      - path: /hello-world-two(/|$)(.*)
        pathType: Prefix
        backend:
          service:
            name: aks-helloworld-two
            port:
              number: 80
      - path: /(.*)
        pathType: Prefix
        backend:
          service:
            name: aks-helloworld-one
            port:
              number: 80

Create the ingress resource using the kubectl:

kubectl apply -f hello-world-ingress.yaml --namespace ingress-basic

Verify certificate

To verify that the certificate was created successfully, use the kubectl describe certificate tls-secret --namespace ingress-basic command.

Test the ingress configuration

Open a web browser to the FQDN of your Kubernetes ingress controller, such as https://mydevsecops.com/hello-world-one and https://mydevsecops.com/hello-world-two

Now the applications are secured using TLS certificate and are reachable using the custom domain. Applications are also getting host based routing using ingress controller.

Renewal

Let’s Encrypt certificates have a validity period of 90 days, and it is recommended to renew them every 60 days to ensure that there is enough time to complete the renewal process before the certificate expires. Let’s Encrypt provides an automated mechanism for renewing certificates called ACME (Automated Certificate Management Environment) protocol, which is supported by Cert-manager.

When you configure Cert-manager to use Let’s Encrypt as a certificate issuer, it will automatically request and renew certificates using the ACME protocol. Cert-manager will handle the entire renewal process, including validating domain ownership and updating the certificate in Kubernetes secrets.

This automatic renewal process ensures that your TLS/SSL certificates are always up to date, reducing the risk of certificate expiration and potential security vulnerabilities.