From 3b4620ade4ee9bed6d6a7f36496266fbdbcdc8ab Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 12 Jul 2026 19:10:34 +1000 Subject: [PATCH] Bump bind-operator to v0.2.0 and deploy companion TSIG API v0.2.0 adds the BindTSIGAPI CRD and companion API that vault-plugin-secrets-bind-tsig calls to create, rotate and delete TSIG keys. This rolls the operator forward and deploys an API instance. - Bump the operator image and the pulled CRD bundle to v0.2.0. - Broaden the operator ClusterRole with deployments, serviceaccounts and roles/rolebindings so the BindTSIGAPI reconciler can deploy the API and its namespaced RBAC. - Add a BindTSIGAPI (bind-tsig-api) in bind-internal; the operator reconciles it into a Deployment, Service, ConfigMap, master-token Secret and RBAC. Keys are created in bind-internal alongside the authoritative cluster. - Add the generated kubeconform schema for BindTSIGAPI. --- apps/base/bind-internal/kustomization.yaml | 1 + .../bind-internal/tsig-api/kustomization.yaml | 6 + .../base/bind-internal/tsig-api/tsig-api.yaml | 27 +++ apps/base/bind-system/deployment.yaml | 2 +- apps/base/bind-system/kustomization.yaml | 2 +- apps/base/bind-system/rbac.yaml | 10 +- .../bind.unkin.net/bindtsigapi_v1alpha1.json | 207 ++++++++++++++++++ 7 files changed, 252 insertions(+), 3 deletions(-) create mode 100644 apps/base/bind-internal/tsig-api/kustomization.yaml create mode 100644 apps/base/bind-internal/tsig-api/tsig-api.yaml create mode 100644 schemas/bind.unkin.net/bindtsigapi_v1alpha1.json diff --git a/apps/base/bind-internal/kustomization.yaml b/apps/base/bind-internal/kustomization.yaml index 3713db0..112b5d8 100644 --- a/apps/base/bind-internal/kustomization.yaml +++ b/apps/base/bind-internal/kustomization.yaml @@ -7,3 +7,4 @@ resources: - authoritative - resolvers - externaldns + - tsig-api diff --git a/apps/base/bind-internal/tsig-api/kustomization.yaml b/apps/base/bind-internal/tsig-api/kustomization.yaml new file mode 100644 index 0000000..4979fc0 --- /dev/null +++ b/apps/base/bind-internal/tsig-api/kustomization.yaml @@ -0,0 +1,6 @@ +--- +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - tsig-api.yaml diff --git a/apps/base/bind-internal/tsig-api/tsig-api.yaml b/apps/base/bind-internal/tsig-api/tsig-api.yaml new file mode 100644 index 0000000..9b09541 --- /dev/null +++ b/apps/base/bind-internal/tsig-api/tsig-api.yaml @@ -0,0 +1,27 @@ +--- +# Companion TSIG API. The operator reconciles this into a Deployment, Service, +# ConfigMap, master-token Secret and namespaced RBAC. vault-plugin-secrets-bind-tsig +# calls it to create/rotate/delete TSIG keys, which it does by managing +# BindTSIGKey resources in this namespace (the operator reconciles the material). +# +# The master access token Secret (bind-tsig-api-token) is generated by the +# operator if absent; a VaultStaticSecret may later pre-seed/overwrite it so the +# token is sourced from Vault rather than generated in-cluster. +apiVersion: bind.unkin.net/v1alpha1 +kind: BindTSIGAPI +metadata: + name: bind-tsig-api + namespace: bind-internal +spec: + image: git.unkin.net/unkin/bind-tsig-api:v0.2.0 + replicas: 1 + port: 8443 + # targetNamespace defaults to this resource's namespace (bind-internal), where + # the authoritative cluster and its keys live. + resources: + requests: + cpu: 25m + memory: 64Mi + limits: + cpu: 250m + memory: 128Mi diff --git a/apps/base/bind-system/deployment.yaml b/apps/base/bind-system/deployment.yaml index 69e77b6..cc4387b 100644 --- a/apps/base/bind-system/deployment.yaml +++ b/apps/base/bind-system/deployment.yaml @@ -21,7 +21,7 @@ spec: runAsNonRoot: true containers: - name: operator - image: git.unkin.net/unkin/bind-operator:v0.1.5 + image: git.unkin.net/unkin/bind-operator:v0.2.0 args: - --metrics-bind-address=:8080 - --health-probe-bind-address=:8081 diff --git a/apps/base/bind-system/kustomization.yaml b/apps/base/bind-system/kustomization.yaml index 9f036fe..20033dc 100644 --- a/apps/base/bind-system/kustomization.yaml +++ b/apps/base/bind-system/kustomization.yaml @@ -6,6 +6,6 @@ resources: - namespace.yaml # CRDs are pulled from the bind-operator repo at the matching tag rather than # vendored here, so they never drift from the operator. - - https://git.unkin.net/unkin/bind-operator/raw/tag/v0.1.5/config/crd/install.yaml + - https://git.unkin.net/unkin/bind-operator/raw/tag/v0.2.0/config/crd/install.yaml - rbac.yaml - deployment.yaml diff --git a/apps/base/bind-system/rbac.yaml b/apps/base/bind-system/rbac.yaml index 628c3ed..37946c9 100644 --- a/apps/base/bind-system/rbac.yaml +++ b/apps/base/bind-system/rbac.yaml @@ -23,7 +23,15 @@ rules: resources: ["pods/exec"] verbs: ["create", "get"] - apiGroups: ["apps"] - resources: ["statefulsets"] + resources: ["statefulsets", "deployments"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + # The BindTSIGAPI reconciler deploys the companion API: a ServiceAccount plus + # a namespaced Role/RoleBinding granting it access to BindTSIGKey + Secrets. + - apiGroups: [""] + resources: ["serviceaccounts"] + verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] + - apiGroups: ["rbac.authorization.k8s.io"] + resources: ["roles", "rolebindings"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - apiGroups: [""] resources: ["events"] diff --git a/schemas/bind.unkin.net/bindtsigapi_v1alpha1.json b/schemas/bind.unkin.net/bindtsigapi_v1alpha1.json new file mode 100644 index 0000000..e86859c --- /dev/null +++ b/schemas/bind.unkin.net/bindtsigapi_v1alpha1.json @@ -0,0 +1,207 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "properties": { + "apiVersion": { + "type": "string" + }, + "kind": { + "type": "string" + }, + "metadata": { + "type": "object" + }, + "spec": { + "properties": { + "env": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "image": { + "default": "git.unkin.net/unkin/bind-tsig-api:latest", + "type": "string" + }, + "imagePullPolicy": { + "type": "string" + }, + "port": { + "default": 8443, + "format": "int32", + "type": "integer" + }, + "replicas": { + "default": 1, + "format": "int32", + "type": "integer" + }, + "resources": { + "properties": { + "claims": { + "items": { + "properties": { + "name": { + "type": "string" + }, + "request": { + "type": "string" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "name" + ], + "x-kubernetes-list-type": "map" + }, + "limits": { + "additionalProperties": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "x-kubernetes-int-or-string": true + }, + "type": "object" + }, + "requests": { + "additionalProperties": { + "anyOf": [ + { + "type": "integer" + }, + { + "type": "string" + } + ], + "pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$", + "x-kubernetes-int-or-string": true + }, + "type": "object" + } + }, + "type": "object" + }, + "service": { + "properties": { + "annotations": { + "additionalProperties": { + "type": "string" + }, + "type": "object" + }, + "externalTrafficPolicy": { + "enum": [ + "Cluster", + "Local" + ], + "type": "string" + }, + "loadBalancerIP": { + "type": "string" + }, + "type": { + "enum": [ + "ClusterIP", + "LoadBalancer", + "NodePort" + ], + "type": "string" + } + }, + "type": "object" + }, + "targetNamespace": { + "type": "string" + }, + "tokenSecretName": { + "type": "string" + } + }, + "type": "object" + }, + "status": { + "properties": { + "conditions": { + "items": { + "properties": { + "lastTransitionTime": { + "format": "date-time", + "type": "string" + }, + "message": { + "maxLength": 32768, + "type": "string" + }, + "observedGeneration": { + "format": "int64", + "minimum": 0, + "type": "integer" + }, + "reason": { + "maxLength": 1024, + "minLength": 1, + "pattern": "^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$", + "type": "string" + }, + "status": { + "enum": [ + "True", + "False", + "Unknown" + ], + "type": "string" + }, + "type": { + "maxLength": 316, + "pattern": "^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$", + "type": "string" + } + }, + "required": [ + "lastTransitionTime", + "message", + "reason", + "status", + "type" + ], + "type": "object" + }, + "type": "array", + "x-kubernetes-list-map-keys": [ + "type" + ], + "x-kubernetes-list-type": "map" + }, + "endpoint": { + "type": "string" + }, + "observedGeneration": { + "format": "int64", + "type": "integer" + }, + "phase": { + "type": "string" + }, + "readyReplicas": { + "format": "int32", + "type": "integer" + }, + "tokenSecret": { + "type": "string" + } + }, + "type": "object" + } + }, + "type": "object" +}