Cert-Manager with Let’s Encrypt & Cloudflare

In this guide, we’ll walk through the steps to set up Cert-Manager on a Kubernetes cluster, configure a Cloudflare API token for DNS-01 challenge validation, and issue SSL/TLS certificates for your domain. This process ensures that your applications are secured with Let’s Encrypt certificates, leveraging Cloudflare for DNS-based verification

Create CRD

Before installing Cert-Manager, it’s essential to apply the required Custom Resource Definitions (CRDs). These CRDs define the custom resources that Cert-Manager uses to manage SSL/TLS certificates and integrate seamlessly with Kubernetes.

Run the following command to install the CRDs

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

Install cert-manager with helm

to simplify we can install cert-manager helm chart with this command

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

$ helm install cert-manager jetstack/cert-manager --version v1.12.0 --namespace cert-manager-playground
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX



Creating a Cloudflare API Token

Once created, save the API token securely, as it will be added to your Kubernetes cluster as a secret.

Adding the Cloudflare API Token to Kubernetes

Create a Kubernetes secret to store your Cloudflare API token. Replace <change_with_your_api_token> with your actual token:

apiVersion: v1
kind: Secret
metadata:
name: cloudflare-api-token
namespace: cert-manager-playground
type: Opaque
stringData:
api-token: <change_with_your_api_token>
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Apply the secret

kubectl apply -f cloudflare-api-secret.yaml
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX


Issuing a Certificate

To issue a certificate, create a Certificate resource. Below is a example for the domain opreks.my.id

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: cloudflare-issuer
spec:
acme:
email: <change_with_your_email>
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: cluster-issuer-account-key
solvers:
- dns01:
cloudflare:
email: <change_with_your_email>
apiTokenSecretRef:
name: cloudflare-api-token
key: api-token
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
# certificate.yml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: certificate-opreks-my-id #<change_with_your_cert_name>
spec:
duration: 2160h # 90d
renewBefore: 360h # 15d
subject:
organizations:
- testorganization
privateKey:
algorithm: RSA
encoding: PKCS1
size: 2048
dnsNames:
- "*.opreks.my.id"
secretName: certificate-opreks-my-id
issuerRef:
name: cloudflare-issuer
kind: ClusterIssuer
group: cert-manager.io
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
$kubectl apply -f cluster-issuer.yaml
$kubectl apply -f certificate.yaml
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
default 0s Normal cert-manager.io CertificateRequest/certificate-opreks-my-id-mtzx8 Certificate request has been approved by cert-manager.io
default 0s Normal OrderCreated CertificateRequest/certificate-opreks-my-id-mtzx8 Created Order resource default/certificate-opreks-my-id-mtzx8-2839657829
default 0s Normal Created Order/certificate-opreks-my-id-mtzx8-2839657829 Created Challenge resource "certificate-opreks-my-id-mtzx8-2839657829-4201251540" for domain "opreks.my.id"
default 0s Normal Started Challenge/certificate-opreks-my-id-mtzx8-2839657829-4201251540 Challenge scheduled for processing
default 0s Normal Presented Challenge/certificate-opreks-my-id-mtzx8-2839657829-4201251540 Presented challenge using DNS-01 challenge mechanism
default 1s Normal DomainVerified Challenge/certificate-opreks-my-id-mtzx8-2839657829-4201251540 Domain "opreks.my.id" verified with "DNS-01" validation
default 0s Normal Complete Order/certificate-opreks-my-id-mtzx8-2839657829 Order completed successfully
default 0s Normal CertificateIssued CertificateRequest/certificate-opreks-my-id-mtzx8 Certificate fetched from issuer successfully
default 0s Normal Issuing Certificate/certificate-opreks-my-id The certificate has been successfully issued
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX



Logs and Validation

Once you’ve applied the configurations, the Cert-Manager will start the certificate issuance process. You can monitor the logs to ensure everything is working as expected. Here’s an example of successful logs:

echo 0s Normal Started Challenge/certificate-opreks-my-id-f45mg-3606330728-3498652677 Challenge scheduled for processing
echo 0s Normal Presented Challenge/certificate-opreks-my-id-f45mg-3606330728-3498652677 Presented challenge using DNS-01 challenge mechanism
echo 0s Normal DomainVerified Challenge/certificate-opreks-my-id-f45mg-3606330728-3498652677 Domain "echo.opreks.my.id" verified with "DNS-01" validation
echo 0s Normal Complete Order/certificate-opreks-my-id-f45mg-3606330728 Order completed successfully
echo 0s Normal CertificateIssued CertificateRequest/certificate-opreks-my-id-f45mg Certificate fetched from issuer successfully
echo 0s Normal Issuing Certificate/certificate-opreks-my-id The certificate has been successfully issued
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX


To validate our Kubernetes setup, we will deploy a simple echo service that responds to HTTP requests. This service will be useful for testing network connectivity and verifying the functionality of our cluster. Please create deployment.yaml file below

apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-service
namespace: echo
labels:
app: echo-service
spec:
replicas: 1
selector:
matchLabels:
app: echo-service
template:
metadata:
labels:
app: echo-service
spec:
containers:
- name: echo-service
image: hashicorp/http-echo:latest
args:
- "-text=Hello, World!"
ports:
- containerPort: 5678
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: echo-ingress
# annotations:
# cert-manager.io/cluster-issuer: "cloudflare-issuer"
namespace: echo
spec:
ingressClassName: nginx-external # Specify the ingress class here
rules:
- host: echo.opreks.my.id
http:
paths:
- backend:
service:
name: echo-service
port:
number: 80
path: /
pathType: ImplementationSpecific
tls:
- hosts:
- echo.opreks.my.id
secretName: certificate-opreks-my-id
---
apiVersion: v1
kind: Service
metadata:
name: echo-service
namespace: echo
spec:
selector:
app: echo-service
ports:
- protocol: TCP
port: 80
targetPort: 5678
type: ClusterIP
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
$kubectl apply -f deployment.yaml
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Testing access from the client using Nmap to verify the certificate

nmap --script ssl-cert -p 443 echo.opreks.my.id
Starting Nmap 7.95 ( https://nmap.org ) at 2023-01-27 13:19 WIB
Nmap scan report for echo.opreks.my.id (54.169.147.155)
Host is up (0.021s latency).
Other addresses for echo.opreks.my.id (not scanned): 13.213.98.236
rDNS record for 54.169.147.155: ec2-54-169-147-155.ap-southeast-1.compute.amazonaws.com
PORT STATE SERVICE
443/tcp open https
| ssl-cert: Subject: commonName=echo.opreks.my.id
| Subject Alternative Name: DNS:echo.opreks.my.id
| Issuer: commonName=R11/organizationName=Let's Encrypt/countryName=US
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2023-01-27T05:17:23
| Not valid after: 2023-04-27T05:17:22
| MD5: c623:85b3:6575:d70a:bf0b:0475:2d68:1cea
|_SHA-1: db9a:17e1:0c12:5720:994b:57f2:b402:8b32:11dc:fe36
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Leave a Reply

Your email address will not be published. Required fields are marked *