The operator-generated TSIG Secret previously carried only the managed-by
label, so it could not be mirrored to another namespace by emberstack
reflector (which requires reflection-allowed annotations on the source).
Add spec.secretTemplate.{annotations,labels}, applied both when the Secret
is first generated and reconciled onto the existing Secret when the CR
changes (imported secrets are left untouched so we don't fight their
external manager). This lets the external-dns TSIG key be managed in
bind-internal and reflected into the externaldns namespace.
The vault-plugin-secrets-bind-tsig plugin needs an HTTP endpoint that
creates, reads, rotates and deletes TSIG keys on its behalf, decoupling
Vault from direct Kubernetes API access. This adds that companion API and
lets the operator deploy it declaratively.
- Add BindTSIGAPI CRD: creating one makes the operator reconcile a
Deployment, Service, ConfigMap (env vars), token Secret and namespaced
RBAC for the companion API. Spec covers image, replicas, port,
targetNamespace, tokenSecretName, extra env, service exposure and
resources.
- Generate the master access token Secret only when absent, so a
VaultStaticSecret may pre-seed/overwrite it; the operator does not own it.
- Add the companion API server (internal/tsigapi): bearer-auth HTTP
contract POST /v1/keys, GET/DELETE /v1/keys/{name}, POST
/v1/keys/{name}/rotate, backed by BindTSIGKey custom resources the
operator reconciles into key material.
- Add cmd/tsigapi entrypoint and Dockerfile.tsigapi (distroless).
- Wire the reconciler into setup, regenerate CRDs/RBAC/deepcopy, and add
Woodpecker build (PR dry-run) and release (tag push) steps for the
bind-tsig-api image.
- Cover the API server with auth and key-lifecycle unit tests.
Secondaries reject RFC2136/nsupdate writes, but the read Service round-
robins across all pods. Add an optional per-cluster write endpoint that
targets only the primary pod (ordinal 0) via the StatefulSet pod-name
label. Reads keep using the all-pods Service.
- api: BindCluster.spec.primaryService (*ClusterServiceSpec) — ClusterIP
for in-cluster writers (external-dns) or LoadBalancer for external
- reconcilePrimaryService creates <cluster>-primary selecting pod-0 when
set, deletes it when unset
- regenerate CRDs + install.yaml
Adds BindCluster.spec.service.externalTrafficPolicy so DNS LoadBalancers
can preserve client source IPs (Local), which the source-IP ACLs on the
authoritative/resolver need to actually restrict external clients (Cluster
SNATs everything to node IPs).
- api: ClusterServiceSpec.externalTrafficPolicy (enum Cluster;Local)
- set it on the client Service for LoadBalancer/NodePort types
- regenerate CRDs + install.yaml
TSIG keys were included in every cluster's keys.conf namespace-wide. When
multiple clusters share a namespace, that leaks keys across clusters. Add
spec.clusterRef so a key can target a specific BindCluster; empty keeps
the shared (all-clusters-in-namespace) behaviour.
- api: BindTSIGKey.spec.clusterRef (optional)
- BindCluster keys.conf now includes only keys with matching or empty
clusterRef
- regenerate CRDs + install.yaml bundle
Adds config/crd/install.yaml (all 9 CRDs concatenated) so downstream
GitOps can reference the CRDs by a single stable raw URL instead of
vendoring copies.
- make generate now writes config/crd/install.yaml from config/crd/bases