Ingress setup for letsencrypt and traefik on k3s

Now I’ve managed to get many services running on my Kubernetes Pi Cluster, I started running into a new qwerk… How can I get all of these services to be accessible via hostnames on standard ports and with zero maintenance TLS certificates that are recognised by mainstream browsers?

This is where Traefik and its cert-manager plugin come into play. Traefik ships with k3s as the default solution for Ingress, (something i’d previously just used nginx for).

Requirements

  • A k3s node / cluster.
  • A domain that you can manage DNS for and supported by the DNS plugin in cert-manager. (I chose Cloudflare DNS for now).
  • A Kubernetes service defined for each of your deployments.

Setting up cert-manager

This is pretty convenient and is simply achieved by applying a remote yaml.

kubectl create ns cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.8.0/cert-manager.yaml

Once this has completed you’ll have access to the new cert-manager CRD’s and can create a new ClusterIssuer. However first we need to add a new secret for our cloudflare DNS plugin.

apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-token-secret
  namespace: cert-manager
type: Opaque
stringData:
  api-token: YOUR_CLOUDFLARE_API_TOKEN

It’s important that the api-token used here has access to Read and Edit the zones you want to create certs for.

Now we can create our ClusterIssuer using the newly stored secret. (You can swap out the uncommented line for Staging/Production).

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-production
  namespace: cert-manager
spec:
  acme:
    email: [email protected]
    #server: https://acme-staging-v02.api.letsencrypt.org/directory
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: issuer-letsencrypt-production
    solvers:
    - dns01:
        cloudflare:
          apiTokenSecretRef:
            key: api-token
            name: cloudflare-api-token-secret
          email: [email protected]

Almost done, we have everything needed to create a Certificate now for your given hostname, even wildcards are supported.

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: wildcard-cert
  namespace: kube-system
spec:
  # Secret names are always required.
  secretName: wildcard-secret
  # At least one of a DNS Name, URI, or IP address is required.
  dnsNames:
  - "*.mikenet.uk"
  issuerRef:
    kind: ClusterIssuer
    name: letsencrypt-production

And thats it. (almost) You should now see happy results if you kubectl describe your ClusterIssuer and Certificate. If so, your certificate has been issued and is working using the DNS plugin! The very last part of the puzzle is to replace the default Traefik self-signed defaultCertificate with your newly created certificate (in my case, wildcard-cert).

apiVersion: traefik.containo.us/v1alpha1
kind: TLSStore
metadata:
  name: default
  namespace: kube-system
spec:
  defaultCertificate:
    secretName: wildcard-secret

Now you can simply setup a new Ingress object for each existing Service and they’ll immediately become accessible via the TLS protected hostname.

kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
  name: mikenet-website
  namespace: default
  annotations:
    traefik.ingress.kubernetes.io/router.entrypoints: web, websecure ## web not needed here.
    traefik.ingress.kubernetes.io/router.tls: "true"
spec:
  rules:
    - host: mikenet.uk # your hostname
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: svc-mikenet-website
                port:
                  number: 4321

Again, this is self-documentation, but perhaps useful for anyone else after a simple setup that requires absolutely no maintenance. The certs will auto renew every 3 months as needed, the only dependency here is your reachabilty to both the Cloudflare API and letsencrypt. There are a ton of DNS and HTTP plugins available for certificate verification that are supported by cert-manager so worth exploring more.