Q: Where do we put the certificate for a mutating/validating webhook?
- A: We put the webhook server TLS cert/key in the webhook Pod, typically as a Kubernetes Secret mounted into the Deployment.
- A: We put the CA certificate (that signed the webhook server cert) into the WebhookConfiguration as
webhooks[].clientConfig.caBundleso the API server can trust the webhook endpoint.
Q: With cert-manager installed (self-signed Issuer), how do we create TLS for the webhook and mount it?
-
A: We create a
Certificate(cert-manager) that writes a TLS Secret (e.g.,webhook-server-tls) containingtls.crtandtls.key. -
A: We mount that Secret into the webhook Deployment (e.g., at
/tls) and configure the webhook server to serve HTTPS using those files. -
A: The certificate must include DNS SANs matching the Service the API server calls, e.g.:
webhook-svc.<ns>.svcwebhook-svc.<ns>.svc.cluster.local
Q: My Secret is in a different namespace. Can we mount it into a Deployment in our namespace?
-
A: No. Kubernetes Secrets are namespaced, and Pods can only mount Secrets from their own namespace.
-
A: The usual fixes are:
- Re-issue the Certificate in our webhook namespace (best for rotation), or
- Copy/sync the Secret into our namespace (one-time copy won’t auto-rotate unless we use a sync mechanism).
Q: Can we fetch “configs directly from etcd” and see what apiVersion is stored?
-
A: Direct etcd reads are possible but not the usual approach:
- Objects are stored in storage encoding (often protobuf/binary) and may be encrypted at rest.
- The “stored apiVersion” is not “whatever we applied”; it’s the cluster’s storage version decision.
-
A: For CRDs, the persisted version is determined by the CRD’s
spec.versions[].storage: true. We can also inspect which versions have been stored historically viastatus.storedVersions.
Webhooks + multi-version CRDs (Kubebuilder): Q&A
Q: Why did our v2 (spoke) defaulting/validating webhook run when we applied a v1 YAML?
-
A: Admission webhooks match resources, and by default
matchPolicybehaves like Equivalent. -
A: With
Equivalent, the API server may:- match a webhook registered for another served version of the same resource, and
- convert the object to the version expected by the webhook before calling it.
-
A: So applying
cache.my.domain/v1can still trigger a webhook registered forv2if the match policy allows equivalence.
Q: In our webhook YAML, we “strongly mentioned apiVersions: [v2]” — why could v1 still trigger it?
- A: Because
apiVersionsin webhook rules is subject tomatchPolicy. - A: If
matchPolicyis Equivalent (explicitly or by default), v1 and v2 can be treated as equivalent for webhook matching, and the API server can call the webhook after converting.
Q: What’s the flow of kubectl apply with conversion + admission webhooks?
-
A: There isn’t a single, fixed “conversion step” between mutating and validating.
-
A: Conversion can happen whenever the API server needs a different version:
- to call a webhook registered for another version (when Equivalent matching applies),
- to store the object in the CRD’s storage version,
- to serve responses/watches to clients/controllers.
-
A: High-level flow:
kubectl→ API server → (maybe convert to webhook’s version) → Mutating → (maybe convert) → Validating → (convert to storage version) → etcd → controller watch → reconciler
Q: After we set matchPolicy: Exact, why did our kubectl apply start failing with decode errors?
-
A: We changed the behavior so the API server will call the webhook only for the exact apiVersion in the request (no equivalence conversion for webhook matching).
-
A: We configured the webhook rules to include both:
apiVersions: [v1alpha1, v1]- but the webhook endpoint path/handler was still effectively a v1alpha1 decoder (kubebuilder-generated handler expects
*v1alpha1.Memcached).
-
A: Result:
-
We applied
cache.my.domain/v1 -
API server called webhook with a v1 object (because Exact)
-
Webhook tried to decode into v1alpha1 Go type
-
Decode failed → admission denied:
unable to decode ... v1 ... into *v1alpha1.Memcached
-
Q: How do we fix that failure?
- A: We have three valid patterns:
-
Single-version admission (recommended)
- Make webhook rules list only the version our webhook code expects (e.g., only
v1alpha1) - Use
matchPolicy: Equivalent(or omit matchPolicy if default is Equivalent) - The API server converts v1 → v1alpha1 before calling the webhook
- Make webhook rules list only the version our webhook code expects (e.g., only
-
True version-specific admission
- Keep
matchPolicy: Exact - Have separate webhook handlers (or separate paths) for
v1alpha1andv1 - Each handler decodes its own version’s Go type
- Keep
-
One handler that supports multiple versions
- Keep
matchPolicy: Exact+ include multipleapiVersions - Update webhook code to detect request version and decode the matching Go type
- Ensure both versions are added to the runtime scheme
- Keep
Q: What was the “main reason” something failed in our case?
- A: We configured the webhook to accept v1 requests (
apiVersions: [v1alpha1, v1]) while the webhook handler was still decoding only into v1alpha1 types. - A: Setting
matchPolicy: Exactmade the mismatch visible immediately because the API server stopped converting v1 payloads into the v1alpha1 form expected by the webhook.