Let’s Encrypt in Kubernetes Cluster

Let’s Encrypt with a DNS Challenge to Cloudflare

The following is a startup guide to deploying cert-manager on a Kubernetes cluster. The key takeaway in this solution is that Eupraxia Labs uses the DNS Challenge capability to avoid exposing port 80, of the NGNIX Kubernetes Ingress Controller (KIC) to the Internet.

Although step-by-step manual instructions appear here, to allow for some experimentation in a development environment, this does not need to be performed manually. This is automatically provisioned with Helm Charts, in a CI/CD Codefresh pipeline, provided by Eupraxia Labs.

Base Install

[centos@vm-controller ~]$ kubectl create namespace cert-manager
[centos@vm-controller ~]$ kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true
[centos@vm-controller ~]$ kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.7.1/deploy/manifests/00-crds.yaml
[centos@vm-controller ~]$ kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.7.1/deploy/manifests/cert-manager.yaml --validate=false

You can confirm it is configured correctly.

  1. Pods are running

     [centos@vm-controller ~]$ kubectl get pods -n cert-manager
    

    will return results that are similar to:

    [centos@vm-controller ~]$ kubectl get po -n cert-manager
    NAME                                       READY   STATUS    RESTARTS   AGE
    cert-manager-5586f99bcb-zw6gj              1/1     Running   0          2d
    cert-manager-cainjector-859b594f4d-w9zjk   1/1     Running   1          2d
    cert-manager-webhook-7ffdc67f8f-x4sws      1/1     Running   0          2d
    
    
  2. Credentials installed

     [centos@vm-controller ~]$ kubectl get crd | grep certmanager
    

    will return results that are similar to:

    [centos@vm-controller ~]$ kubectl get crd | grep certmanager
        certificates.certmanager.k8s.io     2019-01-19T21:54:29Z
        challenges.certmanager.k8s.io       2019-08-06T14:15:48Z
        clusterissuers.certmanager.k8s.io   2019-01-19T21:54:29Z
        issuers.certmanager.k8s.io          2019-01-19T21:54:30Z
        orders.certmanager.k8s.io           2019-08-06T14:15:48Z
    
    
  3. Certificates and Issuers installed

     [centos@vm-controller ~]$ kubectl get issuercertificate -n cert-manager
    

    will return results that are similar to

     NAME                                                      AGE
     issuer.certmanager.k8s.io/cert-manager-webhook-ca         3m
     issuer.certmanager.k8s.io/cert-manager-webhook-selfsign   3m
    
     NAME                                                              AGE
     certificate.certmanager.k8s.io/cert-manager-webhook-ca            3m
     certificate.certmanager.k8s.io/cert-manager-webhook-webhook-tls   3m
    

Create the CloudFlare DNS issuer

  1. Get your CloudFlare Global API Key from the CloudFlare Dashboard
    • https://dash.cloudflare.com/
    • Select Overview
    • Click Get your API key link
    • Click View next to Global API Key
    • Enter your details and pass the I am not a robot challenge and click View
    • Copy the API Key Secret
  2. Issue the following to generate your CloudFlare API Key Secret
API_KEY=$(echo xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx | base64 -)
cat <<EOF | kubectl apply -f -
---
apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-key
  namespace: cert-manager
type: Opaque
data:
  api-key.txt: ${API_KEY}
EOF
  1. Create the ClusterIssuer configuration
EMAIL_ADDRESS="xxxx@xxxx.xxx"
cat <<EOF | kubectl apply -f -
---
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    email: ${EMAIL_ADDRESS}
    privateKeySecretRef:
      name: letsencrypt-staging
    dns01:
      providers:
      - name: cf-dns
        cloudflare:
          email: ${EMAIL_ADDRESS}
          apiKeySecretRef:
            name: cloudflare-api-key
            key: api-key.txt
EOF
  1. Create the Certificate
DOMAIN_NAME="xxxxx.xxxxx.xxx"
cat <<EOF | kubectl apply -f -
---
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: $(echo $DOMAIN_NAME | tr . -)
  namespace: cert-manager
spec:
  secretName: $(echo $DOMAIN_NAME | tr . -)
  issuerRef:
    name: letsencrypt-staging
    kind: ClusterIssuer
  commonName: '${DOMAIN_NAME}'
  dnsNames:
  - ${DOMAIN_NAME}
  acme:
    config:
    - dns01:
        provider: cf-dns
      domains:
        - ${DOMAIN_NAME}
EOF
  1. Confirm it has been created (may take a minute or 2 to generate) by issuing

    [centos@vm-controller ~]$ kubectl describe certificate $(echo $DOMAIN_NAME | tr . -) -n cert-manager
    

    will return results that are similar to

     Name:         xxxx-xxxx-xxx
     Namespace:    cert-manager
     Labels:       <none>
     Annotations:  kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"certmanager.k8s.io/v1alpha1","kind":"Certificate","metadata":{"annotations":{},"name":"xxxx-xxxx-xxx","namespace":"cert-...
     API Version:  certmanager.k8s.io/v1alpha1
     Kind:         Certificate
     Metadata:
       Cluster Name:
       Creation Timestamp:  2019-02-16T22:44:17Z
       Generation:          1
       Resource Version:    3053428
        Self Link:           /apis/certmanager.k8s.io/v1alpha1/namespaces/cert-manager/certificates/xxxx-xxxx-xxx
       UID:                 649849bf-323c-11e9-9371-00237d495614
     Spec:
       Acme:
         Config:
         Dns 01:
             Provider:  cf-dns
         Domains:
             xxxx.xxxx.xxx
       Common Name:  xxxx.xxxx.xxx
       Dns Names:
         xxxx.xxxx.xxx
       Issuer Ref:
         Kind:       ClusterIssuer
         Name:       letsencrypt-staging
       Secret Name:  xxxx-xxxx-xxx
     Status:
       Conditions:
         Last Transition Time:  2019-02-16T22:45:37Z
         Message:               Certificate is up to date and has not expired
         Reason:                Ready
         Status:                True
         Type:                  Ready
       Not After:               2019-05-17T21:45:36Z
     Events:
       Type     Reason         Age                From          Message
       ----     ------         ----               ----          -------
       Normal   OrderCreated   43m                cert-manager  Created Order resource "xxxx-xxxx-xxx-2862881673"
       Normal   OrderComplete  42m                cert-manager  Order "xxxx-xxxx-xxx-2862881673" completed successfully
       Normal   CertIssued     42m                cert-manager  Certificate issued successfully
       Warning  BadConfig      10m (x2 over 10m)  cert-manager  Resource validation failed: spec: Invalid value: "no issuer specified for Issuer '/letsencrypt-staging'": no issuer specified for Issuer '/letsencrypt-staging'
    
    

Remove

[centos@vm-controller ~]$ kubectl delete -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.6/deploy/manifests/cert-manager.yaml
[centos@vm-controller ~]$ kubectl delete -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.6/deploy/manifests/00-crds.yaml
[centos@vm-controller ~]$ kubectl delete namespace cert-manager