Files
bind-operator/config/crd/bases/bind.unkin.net_bindtsigapis.yaml
T
unkinben 53db084c2d
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build Pipeline was successful
Add companion TSIG API and BindTSIGAPI CRD
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.
2026-07-11 12:44:13 +10:00

268 lines
12 KiB
YAML

---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.17.3
name: bindtsigapis.bind.unkin.net
spec:
group: bind.unkin.net
names:
kind: BindTSIGAPI
listKind: BindTSIGAPIList
plural: bindtsigapis
shortNames:
- btapi
singular: bindtsigapi
scope: Namespaced
versions:
- additionalPrinterColumns:
- jsonPath: .status.endpoint
name: Endpoint
type: string
- jsonPath: .status.readyReplicas
name: Ready
type: integer
- jsonPath: .status.phase
name: Phase
type: string
name: v1alpha1
schema:
openAPIV3Schema:
description: |-
BindTSIGAPI deploys the companion TSIG API. Creating one makes the operator
reconcile a Deployment, Service, ConfigMap, token Secret and RBAC for it.
properties:
apiVersion:
description: |-
APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
type: string
kind:
description: |-
Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
type: string
metadata:
type: object
spec:
description: |-
BindTSIGAPISpec configures the companion TSIG API that the operator deploys.
The API exposes an HTTP contract (used by vault-plugin-secrets-bind-tsig) for
creating, rotating and deleting TSIG keys; it does so by managing BindTSIGKey
custom resources, which the operator then reconciles into key material.
properties:
env:
additionalProperties:
type: string
description: Env are extra environment variables rendered into the
API ConfigMap.
type: object
image:
default: git.unkin.net/unkin/bind-tsig-api:latest
description: Image is the companion API container image.
type: string
imagePullPolicy:
description: ImagePullPolicy for the API container.
type: string
port:
default: 8443
description: Port the API listens on. Defaults to 8443.
format: int32
type: integer
replicas:
default: 1
description: Replicas of the API. Defaults to 1.
format: int32
type: integer
resources:
description: Resources for the API container.
properties:
claims:
description: |-
Claims lists the names of resources, defined in spec.resourceClaims,
that are used by this container.
This field depends on the
DynamicResourceAllocation feature gate.
This field is immutable. It can only be set for containers.
items:
description: ResourceClaim references one entry in PodSpec.ResourceClaims.
properties:
name:
description: |-
Name must match the name of one entry in pod.spec.resourceClaims of
the Pod where this field is used. It makes that resource available
inside a container.
type: string
request:
description: |-
Request is the name chosen for a request in the referenced claim.
If empty, everything from the claim is made available, otherwise
only the result of this 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
description: |-
Limits describes the maximum amount of compute resources allowed.
More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
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
description: |-
Requests describes the minimum amount of compute resources required.
If Requests is omitted for a container, it defaults to Limits if that is explicitly specified,
otherwise to an implementation-defined value. Requests cannot exceed Limits.
More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/
type: object
type: object
service:
description: Service controls how the API is exposed (defaults to
ClusterIP).
properties:
annotations:
additionalProperties:
type: string
description: Annotations added to the client-facing Service (e.g.
PureLB/MetalLB hints).
type: object
externalTrafficPolicy:
description: |-
ExternalTrafficPolicy for a LoadBalancer/NodePort Service. Local preserves
client source IPs (required for source-IP ACLs on the DNS servers) but
only routes to nodes running a pod. Defaults to Cluster.
enum:
- Cluster
- Local
type: string
loadBalancerIP:
description: LoadBalancerIP requests a specific address when Type
is LoadBalancer.
type: string
type:
description: Type of the client-facing Service. Defaults to ClusterIP.
enum:
- ClusterIP
- LoadBalancer
- NodePort
type: string
type: object
targetNamespace:
description: |-
TargetNamespace is where the API creates BindTSIGKey resources. Defaults
to the API's own namespace.
type: string
tokenSecretName:
description: |-
TokenSecretName holds the master access token clients present to the API.
The operator generates a token if the Secret does not exist, so a
VaultStaticSecret may pre-seed it instead. Defaults to "<name>-token".
type: string
type: object
status:
description: BindTSIGAPIStatus reports observed API state.
properties:
conditions:
items:
description: Condition contains details for one aspect of the current
state of this API Resource.
properties:
lastTransitionTime:
description: |-
lastTransitionTime is the last time the condition transitioned from one status to another.
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: |-
message is a human readable message indicating details about the transition.
This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: |-
observedGeneration represents the .metadata.generation that the condition was set based upon.
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
with respect to the current state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: |-
reason contains a programmatic identifier indicating the reason for the condition's last transition.
Producers of specific condition types may define expected values and meanings for this field,
and whether the values are considered a guaranteed API.
The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
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:
description: Endpoint is the in-cluster URL clients (Vault) use to
reach the API.
type: string
observedGeneration:
format: int64
type: integer
phase:
type: string
readyReplicas:
format: int32
type: integer
tokenSecret:
description: TokenSecret is the Secret holding the master access token.
type: string
type: object
type: object
served: true
storage: true
subresources:
status: {}