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

To get started with Cloudflare’s Origin CA Issuer all you need is:

  • a k8s-cluster with cert-manager deployed
  • a Cloudflare account

Instructions for installing and source manifests can be found in GitHub repo github.com/cloudflare/origin-ca-issuer. As I prefer using Kustomize for managing k8s I’ve also raised a PR for adding a kustomization.yaml to the repo. Until that gets added I am using my own fork to simplify deployment.

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.

  2. To verify what will be deployed run command kustomize build .

  3. 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
    
  2. Deploy the issuer:

    kubectl apply -n default -f origin-ca-issuer.yaml
    
  3. 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
    
  2. Deploy the certificate:

    kubectl apply -n default -f origin-cert.yaml
    
  3. 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.