The ingress controller supported by the Kubernetes project itself is nginx. And while there are recipes for setting up automated issuing of TLS certificates using free CAs such as Let's Encrypt, there are quite a few steps involved and you will need to deploy additional services to your cluster to make it work.
Meanwhile, the ingress controller for Caddy does it fully-automated, out-of-the-box.
Enable it during install using the onDemandTLS option like so:
$ helm install \ --namespace=caddy-system \ --repo https://caddyserver.github.io/ingress/ \ --atomic \ --set image.tag=v0.1.0 \ --set ingressController.config.onDemandTLS=true \ --set ingressController.config.email=<your-email> \ --set replicaCount=1 \ --version 1.0.0 \ main \ caddy-ingress-controller
The email option is to allow the CA to send expiry notices if your certificate is coming up for renewal. I suppose that doesn't hurt.
Sometimes it's nice to point a domain to localhost and have HTTPS working for it nonetheless – for example, when testing out authentication flows.
I use a combination of tools to achieve this:
- Hitch TLS proxy to proxy traffic from 443 to 80.
- Varnish Cache to proxy traffic from 80 to one or more backends, and possibly rewrite the request.
- ACME Shell script in stateless mode.
In the real world, my domain is pointing to the Kubernetes cluster. But since I don't have nginx running as my ingress controller, I need an actual service to reply to the ACME requests that will be sent to <my-domain>/.well-known/acme-challenge/<key>.
Python to the rescue!
I added a deployment to the Kubernetes cluster with an image set to python:slim-bullseye and simply mounted the script below as /scripts/main.py using a configmap.
from os import environ from http.server import BaseHTTPRequestHandler, HTTPServer PORT = int(environ.get("PORT", 8080)) ACCOUNT_THUMBPRINT = environ["ACCOUNT_THUMBPRINT"] class handler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.send_header("Content-type", "text/plain") self.end_headers() challenge = self.path.rsplit("/")[-1] message = f"{challenge}.{ACCOUNT_THUMBPRINT}" self.wfile.write(bytes(message, "ascii")) with HTTPServer(("", PORT), handler) as server: server.serve_forever()
The deployment is set to run python /scripts/main.py. The account thumbprint is a secret key that you get when you register a session with the ACME shell script.
Kind of complicated – but at least now I can issue a TLS certificate for my domain any time using:
$ acme.sh --issue -d example.com --stateless
The setup would be a little smoother if I had published a ready-to-go container image with the script included.