Reviewed-on: #18
bind-operator
A Kubernetes operator that manages fleets of BIND9 servers declaratively: StatefulSet-backed clusters with primary/secondary replication, and zones, views, TSIG keys, ACLs, catalog zones, RPZ policies and DNSSEC policies as custom resources.
Architecture
Each BindCluster is a StatefulSet plus a headless Service (stable per-pod DNS)
and a client Service. Ordinal-0 is the primary; the remaining pods are
secondaries that replicate via AXFR/IXFR + NOTIFY. A per-pod PVC holds zone
databases and journals.
Zone content is delivered dynamically: the operator execs rndc addzone and
TSIG nsupdate against the primary pod (the same write path external-dns uses).
Cluster-wide config — options, controls, ACLs, views, dnssec-policy blocks
and response-policy clauses — is rendered into a ConfigMap-backed named.conf
and reloaded with rndc reconfig. New zones land on the secondaries
automatically through a catalog zone, so secondaries never need
per-zone reconfiguration.
BindCluster ──> StatefulSet (pod-0 = primary, pod-N = secondaries)
├─ headless Service (pod-0.<cluster>-headless.<ns>.svc…)
├─ client Service (ClusterIP / LoadBalancer)
├─ ConfigMap (named.conf.primary / .secondary + entrypoint)
├─ Secret <cluster>-keys (TSIG key clauses, included by named.conf)
└─ Secret <cluster>-rndc (rndc control key)
BindZone / DNSRecord ──rndc addzone + nsupdate──> primary ──catalog + AXFR──> secondaries
The named.conf is rendered in two variants (primary/secondary); an entrypoint script picks one based on the pod ordinal.
Custom Resources
| Kind | Purpose |
|---|---|
BindCluster |
A set of BIND9 servers. spec.mode: authoritative or resolver. |
BindZone |
A forward/reverse zone (primary/secondary/forward/stub), records inline, optional dynamic-update + DNSSEC + catalog membership. |
DNSRecord |
A single record set applied via TSIG nsupdate — external-dns as a CRD. |
BindView |
A split-horizon view (match-clients, ordering, per-view recursion). |
BindTSIGKey |
A TSIG key; the operator generates material into a Secret (never stored in the CR). spec.secretTemplate stamps extra labels/annotations onto that Secret (e.g. reflection hints to mirror it into another namespace). |
BindACL |
A reusable named address_match_list. |
BindCatalogZone |
A BIND catalog zone so secondaries auto-provision member zones. |
BindPolicy |
A Response Policy Zone (RPZ) / DNS firewall. |
BindDNSSECPolicy |
A dnssec-policy for automated signing. |
See config/samples/ for worked examples.
Migration mapping
The three Puppet-managed BIND roles map onto three BindClusters:
| Puppet role | BindCluster |
Mode |
|---|---|---|
| 3× authoritative masters | auth |
authoritative (pod-0 primary, 2 secondaries) |
| 3× only-resolvers | resolver |
resolver (3 identical recursive servers) |
| 3× external-dns | externaldns |
authoritative (zones set dynamicUpdate for RFC2136 TSIG updates) |
Development
make generate # regenerate deepcopy, CRDs and RBAC from kubebuilder markers
make build # build the operator binary
make test # go test -race
make lint fmt # go vet / gofmt
Local (kind)
kind create cluster --name bind
docker build -t bind-operator:dev -f Dockerfile.operator .
kind load docker-image bind-operator:dev --name bind
kubectl apply -f config/crd/bases/
kubectl apply -f hack/kind/manifests/
kubectl apply -f config/samples/
CI
Woodpecker runs pre-commit (gofmt + vet), test, and a dry-run image build
on pull requests; pushing a v* tag builds and pushes
artifactapi.k8s.syd1.au.unkin.net/docker-internal/bind-operator (and
bind-tsig-api) to the artifactapi local docker registry.
Notes & caveats
- The BIND container image (
spec.image, defaultinternetsystemsconsortium/bind9:9.20) must shipnamed,rndcandnsupdate. The operator projects its config at/etc/bind-operator(leaving the image's own/etc/bind, includingbind.keys, intact) and runsnamed -g -c /run/named/named.conf. - Dynamic updates authenticate with
nsupdate -y; the TSIG secret is passed on the argv of an exec'd process inside the pod. - RPZ IP-trigger encodings (
ip,client-ip,nsip) are emitted verbatim; QNAME and NSDNAME triggers are fully supported.