Introduction
In the previous blog, we covered the basics of Cert-Manager and its role in managing SSL/TLS certificates for Kubernetes. We discussed how Cert-Manager simplifies certificate issuance, renewal, and management for Kubernetes Ingress resources.
In this blog, we will dive into a hands-on tutorial, showcasing how to automate the provisioning of SSL certificates for a Kubernetes application using Cert-Manager. We will explore different types of certificates, including self-signed certificates, certificates from a self-signed CA, and Let’s Encrypt certificates using DNS challenges.
Overview of Steps
We will follow these steps in our hands-on guide:
- Install Sample application.
- Install Cert-Manager.
- Create Self-Signed Certificates.
- Issue Certificates from a Self-Signed CA.
- Request Certificates from Let’s Encrypt(ACME).
- Automate Certificate Management via Ingress Annotations.
All the necessary YAML files used in this blog will be available in the git repository for easy reference.
Prerequisites
- A running Kubernetes cluster (I am using a k3d cluster in this guide).
- NGINX Ingress controller deployed for exposing applications.
In this lab, I am using a Kubernetes environment deployed locally with k3d(K3s cluster). Here’s the setup of our cluster:
Environment Setup
In this lab, I am using a Kubernetes environment deployed locally with k3d(K3s cluster). You can refer previous blog where we compared Kubernetes solutions which you can consider on the laptop.
➜ kubectl get nodes
NAME STATUS ROLES AGE VERSION
k3d-trinity-cluster1-agent-0 Ready <none> 18d v1.30.4+k3s1
k3d-trinity-cluster1-agent-1 Ready <none> 18d v1.30.4+k3s1
k3d-trinity-cluster1-server-0 Ready control-plane,master 18d v1.30.4+k3s1
➜ kubectl get ingressclass
NAME CONTROLLER PARAMETERS AGE
nginx k8s.io/ingress-nginx <none> 18d
Sample Application Deployment
I have deployed a sample application to demonstrate how SSL certificates can be provisioned. Click here for the resource manifests(I am using sample application from istio demo)
➜ kubectl get deploy -n demo-app
NAME READY UP-TO-DATE AVAILABLE AGE
details-v1 1/1 1 1 7m9s
productpage-v1 1/1 1 1 7m9s
ratings-v1 1/1 1 1 7m9s
reviews-v1 1/1 1 1 7m9s
reviews-v2 1/1 1 1 7m9s
reviews-v3 1/1 1 1 7m9s
I am exposing the application via an Ingress resource as shown below:
kubectl apply -f https://raw.githubusercontent.com/pratik705/snapincloud/refs/heads/main/Blogs/cert-manager/ingress_demo_app.yaml
➜ kubectl get ing -n demo-app
NAME CLASS HOSTS ADDRESS PORTS AGE
product-page nginx productpage.local 172.28.0.60 80 2m17s
To confirm access to the application via HTTP:
➜ curl http://productpage.local -I
HTTP/1.1 200 OK
Currently, when we try to access the app via HTTPS, the default self-signed certificate is used by the NGINX Ingress controller:
➜ curl -k https://productpage.local -I
HTTP/2 200
date: Sun, 13 Oct 2024 23:25:47 GMT
content-type: text/html; charset=utf-8
You can observe that this certificate is not trusted because it is self-signed:
➜ curl -vIk https://productpage.local 2>&1 | grep -i "issuer\|subject"
* subject: O=Acme Co; CN=Kubernetes Ingress Controller Fake Certificate
* issuer: O=Acme Co; CN=Kubernetes Ingress Controller Fake Certificate
Next, we will install Cert-Manager and automate certificate generation.
Cert-Manager Installation
We will begin by installing Cert-Manager to automatically manage SSL certificates. Cert-Manager provisions and renews certificates for Kubernetes Ingress resources automatically.
- Step 1: Install Cert-Manager using Helm
➜ helm repo add jetstack https://charts.jetstack.io --force-update
➜ helm upgrade --install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.16.1 -f https://raw.githubusercontent.com/pratik705/snapincloud/refs/heads/main/Blogs/cert-manager/values.yaml
Verify that Cert-Manager is successfully deployed:
➜ kubectl get deploy -n cert-manager
NAME READY UP-TO-DATE AVAILABLE AGE
cert-manager 1/1 1 1 81s
cert-manager-cainjector 1/1 1 1 81s
cert-manager-webhook 1/1 1 1 81s
Types of Certificates We Will Try
We will explore the following types of certificates:
- Self-signed certificates
- Certificates issued by a self-signed CA
- Certificates issued by Let’s Encrypt(ACME) using Route53 DNS
- Automated certificate creation using Ingress annotations
Self-Signed Certificates
We start by creating a self-signed certificate.
- Step 1: Create a Self-Signed ClusterIssuer
➜ kubectl apply -f https://raw.githubusercontent.com/pratik705/snapincloud/refs/heads/main/Blogs/cert-manager/cluster_issuer_self-signed.yaml
Confirm the ClusterIssuer is ready:
➜ kubectl get clusterissuer
NAME READY AGE
selfsigned-issuer True 7s
- Step 2: Create a Self-Signed Certificate for the Application
➜ kubectl apply -f https://raw.githubusercontent.com/pratik705/snapincloud/refs/heads/main/Blogs/cert-manager/certificate_demo_app-self-signed.yaml
Verify the certificate has been issued:
➜ kubectl get cert -n demo-app
NAME READY SECRET AGE
productpage-self-signed True productpage.example.com 3s
- Access the application with the self-signed certificate:
➜ curl -k https://productpage.example.com -I
HTTP/2 200
The certificate is self-signed, as expected:
➜ curl -vIk https://productpage.example.com 2>&1 | grep -i "issuer\|subject"
* subject: CN=productpage.example.com
* issuer: CN=productpage.example.com
Self-signed certificates are useful for testing but are not recommended for production environments.
Certificates Issued by a Self-Signed CA
This method involves creating a CA certificate to issue certificates for the application.
- Step 1: Create a Self-Signed CA Issuer
➜ kubectl apply -f https://raw.githubusercontent.com/pratik705/snapincloud/refs/heads/main/Blogs/cert-manager/issuer_self-signed-CA.yaml
➜ kubectl get issuer -n demo-app
NAME READY AGE
my-ca-issuer True 15h
- Step 2: Create a Certificate Issued by the Self-Signed CA
➜ kubectl apply -f https://raw.githubusercontent.com/pratik705/snapincloud/refs/heads/main/Blogs/cert-manager/certificate_demo_app-self-signed-ca.yaml
Verify the certificate has been issued:
➜ kubectl get cert productpage-self-signed-ca -n demo-app
NAME READY SECRET AGE
productpage-self-signed-ca True productpage123.example.com 15h
- Access the application:
➜ curl -k https://productpage123.example.com -I
HTTP/2 200
Check the issuer of the certificate:
➜ curl -vIk https://productpage123.example.com 2>&1 | grep -i "issuer\|subject"
* subject: CN=productpage123.example.com
* issuer: CN=my-selfsigned-ca
Certificates Issued by Let’s Encrypt(ACME)
Next, we will use Let’s Encrypt with DNS through AWS Route53 to provision production-grade certificates.
- Step 1: Create a Secret for Route 53 Credentials
We need to provide AWS credentials to allow Cert-Manager to complete DNS validation with Route53:
➜ kubectl --namespace cert-manager create secret generic route53 --from-literal="secret-access-key=<AWS_SECRET_ACCESS_KEY>"
- Step 2: Create a Let’s Encrypt ClusterIssuer
Configure Cert-Manager to use Let’s Encrypt to issue certificates via Route53 DNS validation:
kubectl apply -f https://raw.githubusercontent.com/pratik705/snapincloud/refs/heads/main/Blogs/cert-manager/cluster_issuer_route53.yaml
kubectl get clusterissuer route53
NAME READY AGE
route53 True 4h2m
- Step 3: Request Certificates from Let’s Encrypt
Now that the ClusterIssuer is configured, we can request a certificate for our application.
Create a Certificate Resource:
kubectl apply -f https://raw.githubusercontent.com/pratik705/snapincloud/refs/heads/main/Blogs/cert-manager/certificate_demo_app-route53.yaml
Verify the certificate has been issued:
kubectl get cert productpage-route53 -n demo-app
NAME READY SECRET AGE
productpage-route53 True productpage.pbandark.com 3h28m
- Access the application:
➜ curl -Ik https://productpage.pbandark.com
HTTP/2 200
date: Mon, 14 Oct 2024 10:09:31 GMT
content-type: text/html; charset=utf-8
content-length: 2080
strict-transport-security: max-age=31536000; includeSubDomains
Check the issuer of the certificate:
➜ curl -vIk https://productpage.pbandark.com 2>&1 | grep -i "issuer\|subject"
* subject: CN=productpage.pbandark.com
* issuer: C=US; O=Let's Encrypt; CN=R11
TXT record created on route53 for the domain validation:
Automate Certificate Management via Ingress Annotations
To automate SSL/TLS certificate management further, we can use Ingress annotations. So that you don’t need to create certificate resource as it will be created automatically.
➜ kubectl apply -f https://raw.githubusercontent.com/pratik705/snapincloud/refs/heads/main/Blogs/cert-manager/ingress_demo_app_route53_annotation.yaml
Verify the certificate has been issued:
wordpress/blogs/cert-manager …
➜ kubectl get ing -n demo-app
NAME CLASS HOSTS ADDRESS PORTS AGE
product-page nginx productpage.local 172.28.0.60 80 16h
product-page-route53 nginx productpage.pbandark.com 172.28.0.60 80, 443 5h30m
product-page-route53-1 nginx productpage1.pbandark.com 172.28.0.60 80, 443 5h27m
product-page-selfsigned nginx productpage.example.com 172.28.0.60 80, 443 15h
product-page-selfsigned-ca nginx productpage123.example.com 172.28.0.60 80, 443 15h
➜ kubectl get cert productpage1.pbandark.com -n demo-app
NAME READY SECRET AGE
productpage1.pbandark.com False productpage1.pbandark.com 21s
➜ kubectl get cert productpage1.pbandark.com -n demo-app
NAME READY SECRET AGE
productpage1.pbandark.com True productpage1.pbandark.com 106s
- Check the certificate issued by Let’s Encrypt
➜ echo "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZBekNDQSt1Z0F3SUJBZ0lTQTZMMkZoOGY4TUxNQ1BiWTJadDlGY0JuTUEwR0NTcUdTSWIzRFFFQkN3VUEKTU
[...]
RuT1BsQzdTUVpTWW1kdW5yM0JmOWI3N0FpQy9aaWRzdEszNmRSSUxLejdPQTU0PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==" |base64 -d | openssl x509 -noout -issuer -subject
issuer=C = US, O = Let's Encrypt, CN = R10
subject=CN = productpage1.pbandark.com
Conclusion
In this hands-on guide, we successfully set up Cert-Manager in a Kubernetes environment to automate the provisioning and management of SSL/TLS certificates for an application exposed via NGINX Ingress. We explored self-signed certificates, certificates issued by a self-signed CA, and production-grade certificates from Let’s Encrypt using DNS challenges.
With Cert-Manager, you can ensure your Kubernetes applications are secured with trusted SSL/TLS certificates, providing a robust solution for managing certificates automatically.
If you have any query related to this topic, please add a comment or ping me on LinkedIn.