NodeConfig admission validation

NodeConfig admission validation

Admission validation is optional. A separate, unprivileged Deployment runs the same Keel image with --webhook-only. The node DaemonSet does not serve admission requests, so its host privileges and host mounts are never exposed through this service. The webhook-only process starts no node reconciler or facts collector and does not require a node name or host filesystem. Its Service selector targets only app.kubernetes.io/name: keel-webhook.

Validation rules

The webhook validates both creation and updates of NodeConfig resources:

  • spec.type must identify a registered handler in the running image (file or package in the default build). Every handler's Validate method checks its unrendered required parameters. An empty file contents value is valid.
  • Every parameter and service template must parse with Keel's template functions. Admission cannot check node facts or execute templates: unresolved fact names, host paths, permissions and rendered parameter values are checked on the node.
  • Literal service names must be nonempty and contain no leading option marker, path separators, whitespace, control characters or systemd glob patterns. Template service names are checked after rendering by the node runtime.
  • An explicit interval must be positive; omit the field to use the daemon's default interval.
  • Node selectors must follow Kubernetes label-selector rules.
  • Watched ConfigMap names must be valid, nonempty and unique; they refer to the same namespace as the NodeConfig. Existence is not required because creation and deletion are supported triggers.

The CRD itself requires a nonempty type. The extensible registry is validated by this webhook rather than a fixed CRD enum. Unknown parameter keys are permitted for handler extensibility. CR status updates are outside the webhook rule, and NodeConfig deletion is always possible. Dry-run requests are supported and have no side effects.

Run go test ./internal/validation ./internal/templating for the admission and syntax tests. Keep webhook and DaemonSet images on the same release when adding handler types or changing their parameter contracts.

TLS and prerequisites

deploy/webhook.yaml assumes cert-manager and its CA injector are already installed. Use the version approved for your cluster. It creates a namespaced self-signed Issuer, a Certificate for keel-webhook.tak-os.svc and keel-webhook.tak-os.svc.cluster.local, and the keel-webhook-tls Secret. Cert-manager renews the certificate and injects its trust bundle into the ValidatingWebhookConfiguration; controller-runtime watches the mounted TLS certificate files for rotation. The mounted Secret must contain tls.crt and tls.key. The listener uses port 9443 and accepts a --webhook-cert-dir override.

The dedicated service account has no Role or ClusterRole binding. Its projected token is mounted solely so controller-runtime can construct its in-cluster configuration; validation does not read the Kubernetes API. The webhook has no host mount, runs as UID/GID 65532 with a read-only root filesystem, drops all capabilities, and uses RuntimeDefault seccomp.

For a cluster-managed issuer, replace the Issuer and Certificate issuerRef while retaining the Service DNS names. Without cert-manager, provision the TLS Secret and put the issuing CA's PEM bundle in webhooks[].clientConfig.caBundle (base64 encoded), remove the CA-injection annotation, and own renewal and trust rollover. The API server must be able to reach the Service on TCP 443, forwarded to 9443. Adjust any cluster network policy or control-plane firewall accordingly.

Enable admission

Use the same pinned image tag or digest as the DaemonSet in deploy/webhook.yaml. Create the tak-os namespace and install the CRD first as described in deployment, then stage admission separately:

kubectl apply -f deploy/webhook.yaml
kubectl -n tak-os wait certificate/keel-webhook --for=condition=Ready --timeout=120s
kubectl -n tak-os rollout status deployment/keel-webhook --timeout=120s
kubectl apply -f deploy/webhook-admission.yaml
kubectl get validatingwebhookconfiguration keel-nodeconfig \
  -o jsonpath='{.webhooks[0].clientConfig.caBundle}'

Wait until the last command prints a nonempty CA bundle before creating or updating NodeConfigs. Test a valid resource using server-side dry-run, then test that an unknown handler is rejected:

kubectl apply --dry-run=server -f deploy/example/nodeconfigs.yaml
kubectl -n tak-os create --dry-run=server -f - <<'YAML'
apiVersion: keel.tlspu.io/v1
kind: NodeConfig
metadata:
  name: invalid-admission-check
spec:
  type: unknown-handler
YAML

Existing resources are not retroactively validated. Review them before enabling admission: subsequent spec or metadata updates must satisfy current validation. DaemonSet status updates continue even if an existing resource fails the new validation rules.

Availability, updates and removal

The example has two replicas. Place them on distinct available nodes using your cluster's topology-spread or affinity policy if admission availability requires it. The webhook has failurePolicy: Fail: an unavailable server, invalid TLS or empty trust bundle blocks NodeConfig creation and updates. It does not block other resource kinds or NodeConfig status updates. Inspect the Deployment logs, Certificate conditions, Service endpoints and CA bundle when troubleshooting.

During rollout, keep at least one serving replica available. Upgrade the webhook before submitting NodeConfigs that use new handlers, and keep its image aligned with all node agents. Confirm certificate rotation and network reachability in the target environment.

To disable admission, remove its registration before removing its server:

kubectl delete -f deploy/webhook-admission.yaml
kubectl delete -f deploy/webhook.yaml

Deleting the registration also restores NodeConfig writes during an admission outage; node agents retain their runtime validation. Protect permission to edit admission registrations and the webhook TLS Secret as cluster infrastructure.