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

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

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



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>

Apply the secret

kubectl apply -f cloudflare-api-secret.yaml


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
# 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
$kubectl apply -f cluster-issuer.yaml

$kubectl apply -f certificate.yaml
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



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


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
$kubectl apply -f deployment.yaml

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

Leave a Reply

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