Cloudflare's Origin CA Issuer on k8s

If you are using Cloudflare in front of a web service you somehow need to secure the traffic between Cloudflare and your origin. Typical options for achieving this has been issuing a certificate with Let’s Encrypt or using a Cloudflare Origin CA certificate.

A great option for k8s specific use cases is the recently added Origin CA Issuer controller. Used together with cert-manager CertificateRequest feature it enables a fully automatic workflow for both issuing and renewal of Origin CA certificates.

Table of contents

Deploy Origin CA Controller with Kustomize

  1. To deploy with kustomize first create the kustomization.yaml and add the repository as a resource:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - https://github.com/ChrisEke/origin-ca-issuer/deploy

By default the Origin CA issuer will be deployed to namespace origin-ca-issuer.

  1. To verify what will be deployed run command kustomize build .
  2. When ready to deploy to the cluster run the same command, but with a pipe to kubectl for actually applying the config: kustomize build . | kubectl apply -f -

Configure Origin CA API Key

For the Origin CA issuer to be able to issue and renew certificates it needs to be authenticated towards Cloudflare. This is done by adding a special APK key called Origin CA Key as a secret to the k8s-cluster.

  1. To retrieve the API key go to dash.cloudflare.com/profile/api-tokens and in the row for Origin CA Key select View to display the key.
  2. Copy the API key and then add it as a k8s secret:
kubectl create secret -n default generic \
  origin-ca-service-key \
  --from-literal=key='v1.0-...'

Deploy Origin CA Issuer

  1. Create a new file to use as manifest for the Origin CA Issuer, origin-ca-issuer.yaml:
apiVersion: cert-manager.k8s.cloudflare.com/v1
kind: OriginIssuer
metadata:
  name: origin-issuer
spec:
  requestType: OriginECC
  auth:
    serviceKeyRef:
      name: origin-ca-service-key
      key: key
  1. Deploy the issuer:
kubectl apply -n default -f origin-ca-issuer.yaml
  1. To verify that the issuer is ready run command kubectl get originissuer.cert-manager.k8s.cloudflare.com origin-issuer-production -o yaml. Look for the status part of the output, which should look like this if successful:
status:
  conditions:
    - lastTransitionTime: '2021-03-13T14:28:16Z'
      message: OriginIssuer verified and ready to sign certificates
      reason: Verified
      status: 'True'
      type: Ready

Issue a certificate

Now with the Origin CA issuer ready it’s time to create an actual certificate.

  1. Create a new file origin-cert.yaml:
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: origin-cert
spec:
  secretName: origin-cert
  dnsNames:
    - www.example.org
  duration: 168h
  renewBefore: 24h
  issuerRef:
    group: cert-manager.k8s.cloudflare.com
    kind: OriginIssuer
    name: origin-issuer
  1. Deploy the certificate:
kubectl apply -n default -f origin-cert.yaml
  1. Check if the certificate request is successful with command kubectl -n default get certificate origin-cert. Column READY should be True:
NAME          READY   SECRET        AGE
origin-cert   True    origin-cert   10m

Use with Traefik IngressRoute

With the Origin CA issuer configured we’re now able to use the certificate with any Ingress Controller. I’m currently using Traefik and its IngressObject CRD. Below is an example on how I would configure the certificate that was issued in previous step:

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: example-ingressroute
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`www.example.org`)
      kind: Rule
      services:
        - name: example-web-service
          port: 80
  tls:
    secretName: origin-cert

cert-manager, with the help of the Origin CA Issuer, will now automatically keep this certificate renewed.