Secure traffic to your application with Kubernetes and cert-manager
As a DevOps engineer, I am working on the creation of the Production Kubernetes (EKS) cluster with all needed mechanisms for our SaaS, including the TLS certificates issuing for secure communication to and within the k8s cluster.
In this tutorial, I will show how to secure external and internal traffic coming to your application on the Kubernetes cluster by issuing the TLS certificates with the Cert-Manager.
To simplify things let’s introduce the ‘simply’ web app which sits on the Kubernetes cluster, gets external traffic, and talks to DB (internal traffic) which also installed on the same k8s cluster. Let’s make an assumption that we are able to reach the ‘simply’ app on this simply.app.my-company.com domain using the HTTP protocol (port 80).
What I am trying to achieve?
- Secure all external traffic to the ‘simply’ app using TLS certificate from Let’s Encrypt and adding communication through port 443 (HTTPS)
- Secure internal traffic between the ‘simply’ app and DB using the self-signed certificate.
What is NGINX Ingress?
ingress-nginx is an Ingress controller for Kubernetes using NGINX as a reverse proxy and load balancer.
https://github.com/kubernetes/ingress-nginx
To install Nginx Ingress we will use the Helm package manager.
What is cert-manager?
cert-manager is a native Kubernetes certificate management controller. It can help with issuing certificates from a variety of sources, such as Let’s Encrypt, HashiCorp Vault, Venafi, a simple signing key pair, or self signed. It will ensure certificates are valid and up to date, and attempt to renew certificates at a configured time before expiry. It is loosely based upon the work of kube-lego and has borrowed some wisdom from other similar projects such as kube-cert-manager.
So how to achieve this?
Prerequisites
-
Kubernetes cluster (EKS in my case)
-
Ingress-Nginx installed to Kubernetes cluster
-
Cert-Manager Installed to Kubernetes cluster
-
Configured ‘A’ record which points to your Load Balancer created by Ingress, basically, an alias which points to created LB by Ingress.
You can read about the installation and configuration of all those components in details in my post: Building the CI/CD of the Future, NGINX Ingress + Cert-Manager
Configure Cluster Issuer to issue Let’s Encrypt certificates
To obtain Let’s Encrypt certificates we need to create an issuer, it may be Issuer or ClusterIssuer, the difference is that an Issuer is scoped to a single namespace and ClusterIssuer is a cluster-wide version of an Issuer.
Creation of production ‘Cluster Issuer’ for Let’s Encrypt certificates:
We using HTTP Validation and ACME protocol for ClusterIssuer
Deploy cluster-issuer.yaml
Issue a new Let’s Encrypt certificate with cert-manager for simply.app.my-company.com
To issue a new certificate we will create the Ingress-Nginx rule for the simply.app.my-company.com domain, which uses cert-manager and cluster-issuer we created to issue a certificate from Let’s encrypt.
Pay attention to ‘annotations’ and ‘tls’ parts, you can see the magic of integration between ingress and cert-manager by using annotations.
I make an assumption that you can reach the ‘simply’ app inside of the cluster via the ‘simply-svc’ service and port 80.
Deploy simply-ingress.yaml
Now you need to wait till Cert-Manager acquires a certificate for the simply.app.my-company.com domain, it may take some time. When the certificate will be acquired you will be able to reach simply.app.my-company.com using HTTPS.
You can check acquired certificates and status using ‘kubectl’:
Issue self-signed certificate (signed by CA) with cert-manager for securing internal traffic between ‘simply’ and DB
I will make an assumption that ‘simply’ will be deployed to ‘simplyns’ namespace:
Create self-signed certificate issuer:
notice that Issuer is scoped to a single namespace
Create a CA certificate that will be used to sign other certificates:
‘isCA’ must be true
Check generated certificate/secret created:
Create an additional Issuer to issuer certificates signed by CA
notice that we using previously generated for CA secret
And finally, generate a certificate signed by CA for ‘simply’ and DB communication:
simply deployed to simplyns namespace
db deployed to dbns namespace
‘simply.simplyns.svc.cluster.local’ is internal dns domain of ‘simply’
‘db.dbns.svc.cluster.local’ is internal dns domain of DB
You can check generated secret is created:
Please subscribe to my YT channel
Please follow me on Twitter (@warolv)