cert-manager Webhook for the mymh.dev / homelab system
  • Go 96.2%
  • Dockerfile 3.8%
Find a file
2026-08-25 12:07:46 +02:00
.gitignore Initial commit: cert-manager ACME DNS-01 solver webhook 2026-08-24 13:17:37 +02:00
Dockerfile Initial commit: cert-manager ACME DNS-01 solver webhook 2026-08-24 13:17:37 +02:00
go.mod Initial commit: cert-manager ACME DNS-01 solver webhook 2026-08-24 13:17:37 +02:00
go.sum Initial commit: cert-manager ACME DNS-01 solver webhook 2026-08-24 13:17:37 +02:00
LICENSE.md Initial commit: cert-manager ACME DNS-01 solver webhook 2026-08-24 13:17:37 +02:00
main.go Enforce ClusterIssuer-only invariant on Secret lookup 2026-08-25 12:07:46 +02:00
README.md Initial commit: cert-manager ACME DNS-01 solver webhook 2026-08-24 13:17:37 +02:00

cert-manager-webhook

A cert-manager ACME DNS-01 solver webhook. Currently implements the DNS provider's REST API needed for this project's own zone; see the decision record in homelab-docs (ADR-022) for why DNS-01 and this provider were chosen, this README only covers how to build and run what's here.

Not part of the mymh.dev platform's deployment manifests, those live in homelab-gitops as usual. This repo is only the webhook's own source and image build.


Prerequisites

  • Docker (the build runs entirely inside a container, no local Go install needed)
  • A Kubernetes cluster with cert-manager already installed, to actually deploy and test against

Build

docker build -t cert-manager-webhook:local .

Multi-stage build: compiles with golang:1.26-bookworm, ships as a distroless/static-debian12 runtime image. go.sum is committed, so the build downloads pinned dependency versions only, no go mod tidy at build time.

Why go.mod looks big for such a small tool

Only a handful of packages are imported directly, but cmd.RunWebhookServer (cert-manager's own helper for building this kind of webhook) is built on k8s.io/apiserver, the same library Kubernetes itself uses for its API server. That's what provides the TLS-serving and auth-delegation machinery for free, correctly, without hand-rolling it, at the cost of a large transitive dependency tree (etcd client code, gRPC, OpenTelemetry, none of it actually used directly). Every comparable cert-manager DNS-01 webhook has this same shape, it's the cost of the tool, not this codebase. This isn't npm-style risk either: Go compiles to one static binary at build time, nothing loads or resolves dependencies at runtime, and go.sum is a security feature, not bloat, it cryptographically pins the exact hash of every module version so tampering fails the build immediately.

Configuration

The webhook needs two things at runtime:

  • GROUP_NAME environment variable: the custom API group this webhook registers under (must match the groupName set in the Issuer/ClusterIssuer's webhook solver config).
  • --tls-cert-file / --tls-private-key-file flags: paths to a serving certificate for the webhook's own aggregated-API endpoint. Required explicitly, the underlying server framework does not fail or warn if these are omitted, it silently falls back to an ephemeral self-signed localhost certificate instead (see lessons_learned.md in homelab-docs for the full symptom trail).

It also needs a Kubernetes Secret holding the DNS provider's API token, referenced by name via the Issuer's own solver config (apiKeySecretRef), read from the namespace of the resource being processed.

Local test loop

An isolated kind cluster with cert-manager installed, the built image loaded via kind load docker-image, and a real Certificate requested against Let's Encrypt's staging endpoint is the fastest way to validate a change without touching any real infrastructure or burning a production rate limit. Full worked example (RBAC, the webhook's own serving cert via cert-manager itself, an Issuer, a test Certificate) is documented in homelab-docs's journal alongside this project's own validation of this exact webhook.

License

See LICENSE.md.