Automating Certs Renewal In Kubernetes Using Letsencrypt, certmanager And Cloudflare

What’s Lets-Encrypt?

Let’s Encrypt is a non-profit certificate authority run by Internet Security Research Group (ISRG) that provides X.509 certificates for Transport Layer Security (TLS) encryption at no charge. The certificate is valid for 90 days, during which renewal can take place at any time.

What’s Cert-Manager?

cert-manager is a Kubernetes add-on to automate the management and issuance of TLS certificates from various issuing sources(Lets-Encrypt is one of these sources). It will ensure certificates are valid and up to date periodically, and attempt to renew certificates at an appropriate time before expiry. Cert-manager is based on kube-lego

Why did we choose DNS as challenge type for Let’s Encrypt

Let’s Encrypt supports various types of challenges types, We chose to use DNS-01 challenge instead of more popular HTTP-01 challenge for the following reasons DNS challenge type supports wildcard certificates. HTTP-01 challenge issue certificate for every subdomain and that can cause problems sometimes (Too many lets-encrypt calls when your infra grows, Your API calls can get rate-limited by Let’s Encrypt ) It works well even if you have multiple web servers.

So, Let’s begin!

Create cert-manager namespace

kubectl create namespace cert-manager

Install cert-manager

kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.11.1/cert-manager.yaml

Get the Global token from Cloudflare Get base64 string from Cloudflare global token key using the following command

echo -n "YourGlobalTokenHere" | base64

Create file create-secret.yaml

---
apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-key-secret
  namespace: cert-manager
type: Opaque
data:
  cloudflare-api-key: EnterBase64StringFromPreviousStep

Create file prod-cluster-issuer.yaml

---
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
 labels:
   name: letsencrypt-prod
 name: letsencrypt-prod
spec:
 acme:
   email: [email protected]   # Please enter a valid email id
   server: https://acme-v02.api.letsencrypt.org/directory
   privateKeySecretRef:
     name: letsencrypt-prod
   solvers:
   - selector: {}    
     dns01:
        cloudflare:
          email: [email protected]    
          apiKeySecretRef:
            name: cloudflare-api-key-secret
            key: cloudflare-api-key

Create prod-cluster-certificate.yaml

apiVersion: cert-manager.io/v1alpha2
kind: Certificate
metadata:
  name: example-com
  namespace: default
spec:
  secretName: example-prod-gateway-tls
  duration: 2160h # 90d
  renewBefore: 360h # 15d
  commonName: '*.example.com'
  dnsNames:
  - '*.example.com'
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer

Let’s create a sample nginx-ingress file. (expose.yaml)

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: example-prod-gateway
  annotations:
    kubernetes.io/ingress.class: nginx
    certmanager.k8s.io/cluster-issuer: "letsencrypt-prod"
    kubernetes.io/tls-acme: "true"
    nginx.org/server-snippets: "gzip on;"
    nginx.ingress.kubernetes.io/use-regex: "true"
spec:
  rules:
    - host: test.example.com
      http:
        paths:
          - path: /
            backend:
              serviceName: dex
              servicePort: 80
    - host: trial.example.com
      http:
        paths:
          - path: /
            backend:
              serviceName: hello-world
              servicePort: 80
  tls:
    - secretName: example-prod-gateway-tls
      hosts:
        - test.example.com
        - trial.example.com 
        
kubectl create -f expose.yaml
kubectl create -f create-secret.yaml 
kubectl create -f prod-cluster-issuer.yaml 
kubectl create -f prod-cluster-certificate.yaml

Cert-manager should be able to get new certificates from Lets-Encrypt within 2 mins!

Troubleshooting