Documentation
¶
Overview ¶
Package webhook provides a pure library for Kubernetes admission webhooks.
This package implements an HTTPS webhook server with flexible validation handlers, without dependencies on other project packages. It can be used in any Kubernetes controller project.
The package provides:
- Generic webhook server with configurable validation
- AdmissionReview v1 request/response handling
- ValidationContext with full admission request details
- Thread-safe concurrent request handling
External dependencies required (not provided by this library):
- TLS certificates (from cert-manager, Kubernetes Secret, or Helm)
- ValidatingWebhookConfiguration (via Helm chart or kubectl apply)
Example usage:
// Load certificates from external source (Kubernetes Secret)
secret, err := client.CoreV1().Secrets("default").Get(ctx, "webhook-certs", metav1.GetOptions{})
certPEM := secret.Data["tls.crt"]
keyPEM := secret.Data["tls.key"]
// Create webhook server
server := webhook.NewServer(&webhook.ServerConfig{
Port: 9443,
CertPEM: certPEM,
KeyPEM: keyPEM,
})
// Register validator with full context
server.RegisterValidator("networking.k8s.io/v1.Ingress", func(ctx *webhook.ValidationContext) (bool, string, []string, error) {
// Validation logic with access to operation type and old/new objects
if ctx.Operation == "UPDATE" && ctx.OldObject != nil {
// Implement immutability checks
}
return true, "", nil, nil
})
// Start server
server.Start(ctx)
Index ¶
- type Server
- func (s *Server) Addr() string
- func (s *Server) InstallValidatorGeneration(validators map[string]ValidationFunc, onUnregisteredGVK func(gvk string), ...) (*ValidatorGeneration, error)
- func (s *Server) Listening() <-chan struct{}
- func (s *Server) RegisterValidator(gvk string, fn ValidationFunc)
- func (s *Server) ReplaceValidatorGeneration(validators map[string]ValidationFunc, onUnregisteredGVK func(gvk string), ...) error
- func (s *Server) RetireValidatorGenerationIfCurrent(generation *ValidatorGeneration) error
- func (s *Server) SetOnUnregisteredGVK(fn func(gvk string))
- func (s *Server) SetValidators(validators map[string]ValidationFunc)
- func (s *Server) Shutdown(ctx context.Context) error
- func (s *Server) Start(ctx context.Context) error
- func (s *Server) Validating() bool
- func (s *Server) WaitForQuiet(ctx context.Context, quietPeriod time.Duration) error
- type ServerConfig
- type ValidationContext
- type ValidationFunc
- type ValidatorGeneration
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is an HTTPS webhook server that validates Kubernetes resources.
The server handles AdmissionReview requests from the Kubernetes API server and calls registered validation functions to determine whether resources should be admitted.
The server is thread-safe and can handle multiple concurrent requests.
The certificate is resolved per TLS handshake through getCertificate. When ServerConfig.CertDir is set, that callback reloads tls.crt/tls.key from disk on change, so a rotated certificate (cert-manager renewal written to a mounted Secret) is served without restarting. Otherwise getCertificate returns a fixed certificate parsed once from CertPEM/KeyPEM. Either way the certificate is validated eagerly in NewServer so a malformed cert surfaces there rather than at the first handshake.
func NewServer ¶
func NewServer(input *ServerConfig) (*Server, error)
NewServer creates a new webhook server with the given configuration.
The server will not start until Start() is called. The certificate is loaded eagerly — from CertDir (reloading, on change) or from CertPEM/KeyPEM (fixed) — so configuration errors surface here rather than at the first TLS handshake.
func (*Server) Addr ¶
Addr returns the address the listener actually bound, or "" before it has. Callers that configure Port 0 — tests, which must not fight over a fixed port — read the kernel-assigned port from here once Listening() has closed.
func (*Server) InstallValidatorGeneration ¶
func (s *Server) InstallValidatorGeneration( validators map[string]ValidationFunc, onUnregisteredGVK func(gvk string), onRetired func(), ) (*ValidatorGeneration, error)
InstallValidatorGeneration is ReplaceValidatorGeneration returning the installed generation, which the installer hands to RetireValidatorGenerationIfCurrent when it stops.
func (*Server) Listening ¶
func (s *Server) Listening() <-chan struct{}
Listening returns a channel that is closed once the TLS listener has been bound to the configured port. Until this channel is closed, admission requests sent to the server's address fail with "connection refused". The controller uses this signal so its Pod readiness probe only flips healthy after the webhook is actually reachable.
func (*Server) RegisterValidator ¶
func (s *Server) RegisterValidator(gvk string, fn ValidationFunc)
RegisterValidator registers a validation function for a specific resource type.
The gvk parameter should be in the format "version.Kind" (e.g., "v1.Ingress"). For resources with a group, use "group/version.Kind" (e.g., "networking.k8s.io/v1.Ingress").
If a validator is already registered for this gvk, it will be replaced.
This method is thread-safe.
func (*Server) ReplaceValidatorGeneration ¶
func (s *Server) ReplaceValidatorGeneration( validators map[string]ValidationFunc, onUnregisteredGVK func(gvk string), onRetired func(), ) error
ReplaceValidatorGeneration installs one complete table and retires the old table after all requests that acquired it have returned.
func (*Server) RetireValidatorGenerationIfCurrent ¶
func (s *Server) RetireValidatorGenerationIfCurrent(generation *ValidatorGeneration) error
RetireValidatorGenerationIfCurrent empties the table only while generation is still the installed one. An iteration that stops after its successor installed the next generation must leave that one serving.
func (*Server) SetOnUnregisteredGVK ¶
SetOnUnregisteredGVK REPLACES the unregistered-GVK reporter.
A server that outlives the wiring which built its validator table (the controller keeps one listener bound across config reinitializations, so an admission request never meets a closed port) must be able to re-point this callback at the current wiring's metrics recorder. It is read under the same lock as the validator table, so it swaps atomically with respect to in-flight requests.
func (*Server) SetValidators ¶
func (s *Server) SetValidators(validators map[string]ValidationFunc)
SetValidators atomically replaces the validator table.
func (*Server) Shutdown ¶
Shutdown closes the listener and finishes active responses before retiring validators. The caller must keep validator dependencies alive until it returns.
func (*Server) Start ¶
Start starts the HTTPS webhook server.
The server binds to the configured port synchronously, closes the channel returned by Listening() to signal readiness, and then serves in a background goroutine. The method blocks until the server is shut down (context cancellation) or the serve loop returns an error.
Splitting bind from serve matters because the Pod readiness probe must not flip healthy until admission is reachable — otherwise the API server starts routing AdmissionReview requests at the controller before net.Listen has returned, and every request bounces with "connection refused" until the listener finally binds. Callers that need to gate on the bind read Listening().
func (*Server) Validating ¶
Validating reports whether a caller-installed validator generation answers requests. A server without one denies every request.
type ServerConfig ¶
type ServerConfig struct {
// Port is the HTTPS port to listen on.
// Default: 9443
Port int
// BindAddress is the address to bind to.
// Default: "0.0.0.0"
BindAddress string
// CertDir, when set, is a directory containing tls.crt and tls.key. The
// server reads them through a reloading GetCertificate callback, so a
// rotated certificate (e.g. a cert-manager renewal written to a mounted
// Secret) is picked up on the next handshake without a restart. Takes
// precedence over CertPEM/KeyPEM.
CertDir string
// CertPEM is the PEM-encoded server certificate.
// Required unless CertDir is set.
CertPEM []byte
// KeyPEM is the PEM-encoded private key.
// Required unless CertDir is set.
KeyPEM []byte
// Path is the URL path for the webhook endpoint.
// Default: "/validate"
Path string
// ReadTimeout is the maximum duration for reading the entire request.
// Default: 10s
ReadTimeout time.Duration
// WriteTimeout is the maximum duration before timing out writes of the response.
// Default: 10s
WriteTimeout time.Duration
// IdleTimeout must exceed the API server's idle timeout, or we close first
// and race its next request into an EOF; zero means ReadTimeout (net/http).
// Default: 120s
IdleTimeout time.Duration
// OnUnregisteredGVK, when set, is called for an AdmissionReview whose GVK
// has no registered validator. The request is denied because the API server
// routed it to this validation endpoint without a matching validator.
OnUnregisteredGVK func(gvk string)
}
ServerConfig configures the webhook HTTPS server.
type ValidationContext ¶
type ValidationContext struct {
// Object is the resource object being validated (new version).
// For CREATE: the object being created
// For UPDATE: the new version of the object
// For DELETE: nil when the API server supplies only OldObject
// Stored as unstructured.Unstructured (same type as resource stores use).
Object *unstructured.Unstructured
// OldObject is the existing version of the resource (for UPDATE/DELETE operations).
// For CREATE: nil
// For UPDATE: the current version in the cluster
// For DELETE: the object being deleted (same as Object)
// Stored as unstructured.Unstructured (same type as resource stores use).
OldObject *unstructured.Unstructured
// Operation indicates the admission operation type.
// Values: "CREATE", "UPDATE", "DELETE", "CONNECT"
Operation string
// Namespace is the namespace of the resource (empty for cluster-scoped resources).
Namespace string
// Name is the name of the resource.
// May be empty for CREATE operations using generateName.
Name string
// UID is a unique identifier for this admission request.
// Can be used for correlation and logging.
UID string
// UserInfo contains information about the user making the request.
// Includes username, UID, groups, and extra fields.
// Can be used for authorization decisions.
UserInfo authenticationv1.UserInfo
}
ValidationContext provides the complete context for validating a Kubernetes resource.
This includes the resource object, operation type, and related metadata from the AdmissionRequest. This allows validators to make informed decisions based on the full context of the admission request.
type ValidationFunc ¶
type ValidationFunc func(ctx *ValidationContext) (allowed bool, reason string, warnings []string, err error)
ValidationFunc is called to validate a Kubernetes resource admission request.
Parameters:
- ctx: The validation context with full admission request information
Returns:
- allowed: Whether the resource should be admitted
- reason: Human-readable reason for denial (empty if allowed)
- warnings: Soft warnings surfaced via AdmissionResponse.Warnings. Returned for both allowed and denied responses so kubectl prints them as "Warning:" lines without blocking admission. Each entry should fit in 256 chars; over that, the API server truncates.
- err: Error during validation (500 response if non-nil)
The function receives complete context including both old and new objects, operation type, and metadata. This allows validators to implement sophisticated validation logic based on the admission operation.
Example:
func validateIngress(ctx *webhook.ValidationContext) (bool, string, []string, error) {
// Access new object (already unstructured.Unstructured)
if ctx.Object == nil {
return false, "", nil, errors.New("object is nil")
}
// For UPDATE operations, compare with old object
if ctx.Operation == "UPDATE" && ctx.OldObject != nil {
// Both ctx.Object and ctx.OldObject are *unstructured.Unstructured
// Validate the change...
}
spec, found, err := unstructured.NestedMap(ctx.Object.Object, "spec")
if err != nil || !found {
return false, "spec is required", nil, nil
}
return true, "", nil, nil
}
type ValidatorGeneration ¶
type ValidatorGeneration struct {
// contains filtered or unexported fields
}
ValidatorGeneration is one installed validator table; the installer keeps it to retire only its own.