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.