webhook

package
v0.1.0-alpha.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 30, 2025 License: Apache-2.0 Imports: 13 Imported by: 0

README

pkg/controller/webhook - Webhook Adapter Component

Event adapter component that bridges the pure webhook library to the controller architecture.

Overview

The webhook adapter manages the complete lifecycle of Kubernetes admission webhooks:

  • TLS certificate generation and rotation
  • HTTPS webhook server lifecycle
  • Dynamic ValidatingWebhookConfiguration management
  • Integration with controller event bus
  • Bridging webhook validation to controller validators

This is a coordination layer - the actual webhook functionality lives in pkg/webhook (pure library).

Component Architecture

pkg/webhook/ (Pure Library)
    ├── CertificateManager
    ├── Server
    └── ConfigManager
         ↓
    wrapped by
         ↓
pkg/controller/webhook/ (Event Adapter)
    └── Component
         ├── Manages lifecycle
         ├── Publishes events
         └── Bridges to validators

Usage

Basic Setup
import (
    "haptic/pkg/controller/webhook"
    "haptic/pkg/webhook"
)

// Create component
webhookComponent := webhook.New(
    kubeClient,
    eventBus,
    logger,
    webhook.Config{
        Namespace:   "default",
        ServiceName: "my-webhook-svc",
        Rules: []webhook.WebhookRule{
            {
                APIGroups:   []string{""},
                APIVersions: []string{"v1"},
                Resources:   []string{"configmaps"},
            },
        },
    },
)

// Start component (blocks until context cancelled)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

go webhookComponent.Start(ctx)
Configuration
config := webhook.Config{
    // Required
    Namespace:   "default",
    ServiceName: "webhook-service",

    // Optional (defaults shown)
    WebhookConfigName: "haptic-webhook",
    Port:              9443,
    Path:              "/validate",
    CertRotationCheckInterval: 24 * time.Hour,

    // Validation rules
    Rules: []webhook.WebhookRule{
        {
            APIGroups:   []string{""},
            APIVersions: []string{"v1"},
            Resources:   []string{"configmaps"},
        },
    },
}
Graceful Shutdown
// Stop webhook component
if err := webhookComponent.Stop(ctx); err != nil {
    log.Error("Failed to stop webhook", "error", err)
}

Events Published

The component publishes events to the EventBus for observability and coordination:

Lifecycle Events
  • WebhookServerStartedEvent: Server started successfully
  • WebhookServerStoppedEvent: Server stopped
Certificate Events
  • WebhookCertificatesGeneratedEvent: Initial certificates generated
  • WebhookCertificatesRotatedEvent: Certificates rotated
Configuration Events
  • WebhookConfigurationCreatedEvent: ValidatingWebhookConfiguration created
  • WebhookConfigurationUpdatedEvent: ValidatingWebhookConfiguration updated
Validation Events
  • WebhookValidationRequestEvent: Admission request received
  • WebhookValidationAllowedEvent: Resource admitted
  • WebhookValidationDeniedEvent: Resource rejected
  • WebhookValidationErrorEvent: Validation error

Certificate Rotation

Certificates are automatically rotated when they approach expiration:

  1. Periodic Check: Every 24 hours (configurable)
  2. Rotation Threshold: 30 days before expiry (from certificate manager)
  3. Rotation Process:
    • Generate new certificates
    • Update ValidatingWebhookConfiguration with new CA bundle
    • Restart server with new certificates
    • Publish rotation event

Integration with Controller

The webhook component is typically started in controller initialization:

// After config loaded and EventBus started
webhookComponent := webhook.New(kubeClient, eventBus, logger, webhookConfig)
go webhookComponent.Start(ctx)

Kubernetes Resources Required

Service

The webhook server needs a Kubernetes Service to receive requests from the API server:

apiVersion: v1
kind: Service
metadata:
  name: my-webhook-svc
  namespace: default
spec:
  selector:
    app: my-controller
  ports:
    - port: 443
      targetPort: 9443
      protocol: TCP
RBAC

The controller needs permissions to manage ValidatingWebhookConfiguration:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: webhook-manager
rules:
  - apiGroups: ["admissionregistration.k8s.io"]
    resources: ["validatingwebhookconfigurations"]
    verbs: ["get", "create", "update", "patch", "delete"]

Troubleshooting

Webhook Not Receiving Requests

Check:

  1. Service exists and selects correct pods
  2. ValidatingWebhookConfiguration exists
  3. CA bundle matches generated CA certificate
  4. Network policies allow API server → webhook traffic

Debug:

# Verify service
kubectl get svc my-webhook-svc -o yaml

# Verify webhook configuration
kubectl get validatingwebhookconfigurations my-webhook -o yaml

# Check webhook server logs
kubectl logs deployment/my-controller | grep webhook
Certificate Errors

Symptoms: x509: certificate signed by unknown authority

Fix: Ensure CA bundle in ValidatingWebhookConfiguration matches the CA certificate used to sign server certificate.

Check:

# View CA bundle
kubectl get validatingwebhookconfigurations my-webhook \
    -o jsonpath='{.webhooks[0].clientConfig.caBundle}' | base64 -d
Port Conflicts

Symptoms: address already in use

Fix: Change webhook port in configuration or verify no other process uses port 9443.

Advanced Topics

Custom Validation

To wire webhook to existing validators (Phase 2 feature):

// Create validator bridge
func (c *Component) createValidator(gvk string) webhook.ValidationFunc {
    return func(obj interface{}) (bool, string, error) {
        // Publish validation request event
        req := events.NewConfigValidationRequest(obj)

        // Use request-response pattern to gather validation results
        result, err := c.eventBus.Request(ctx, req, events.RequestOptions{
            Timeout: 5 * time.Second,
            ExpectedResponders: []string{"basic", "template"},
        })

        // Aggregate results
        if err != nil || !allValid(result) {
            return false, extractReason(result), nil
        }

        return true, "", nil
    }
}
Multiple Webhook Rules

Define separate rules for different resource types:

Rules: []webhook.WebhookRule{
    {
        APIGroups:   []string{""},
        APIVersions: []string{"v1"},
        Resources:   []string{"configmaps"},
        Operations:  []admissionv1.OperationType{admissionv1.Create, admissionv1.Update},
    },
    {
        APIGroups:   []string{""},
        APIVersions: []string{"v1"},
        Resources:   []string{"secrets"},
        Operations:  []admissionv1.OperationType{admissionv1.Create},
    },
}
Failure Policy

Control what happens when webhook is unavailable:

// Fail-closed (reject requests if webhook down)
fail := admissionv1.Fail
rule.FailurePolicy = &fail

// Fail-open (allow requests if webhook down)
ignore := admissionv1.Ignore
rule.FailurePolicy = &ignore

See Also

  • Pure webhook library: pkg/webhook/README.md
  • Webhook event types: pkg/controller/events/README.md
  • Controller architecture: docs/development/design.md

Documentation

Overview

Package webhook provides the webhook adapter component that bridges the pure webhook library to the event-driven controller architecture.

The webhook component manages the lifecycle of admission webhooks including:

  • HTTPS webhook server
  • Integration with controller validators

Note: TLS certificates are fetched from Kubernetes Secret via API. ValidatingWebhookConfiguration is created by Helm at installation time.

Index

Constants

View Source
const (
	// BasicValidatorComponentName is the unique identifier for the basic validator component.
	BasicValidatorComponentName = "basic-validator"

	// BasicValidatorID identifies the basic validator in scatter-gather responses.
	BasicValidatorID = "basic"
)
View Source
const (
	// ComponentName is the unique identifier for this component.
	ComponentName = "webhook"

	// DefaultWebhookPort is the default HTTPS port for the webhook server.
	DefaultWebhookPort = 9443

	// DefaultWebhookPath is the default URL path for validation requests.
	DefaultWebhookPath = "/validate"

	// EventBufferSize is the size of the event subscription buffer.
	EventBufferSize = 50
)

Variables

This section is empty.

Functions

func ExtractWebhookRules

func ExtractWebhookRules(cfg *config.Config) []webhook.WebhookRule

ExtractWebhookRules extracts webhook rules from controller configuration.

It iterates through watched resources and creates webhook rules for resources with enable_validation_webhook: true.

Parameters:

  • cfg: Controller configuration containing watched resources

Returns:

  • Slice of webhook rules for resources that have validation enabled
  • Empty slice if no resources have webhook validation enabled

func HasWebhookEnabled

func HasWebhookEnabled(cfg *config.Config) bool

HasWebhookEnabled checks if any watched resources have webhook validation enabled.

Types

type BasicValidatorComponent

type BasicValidatorComponent struct {
	// contains filtered or unexported fields
}

BasicValidatorComponent performs basic structural validation of Kubernetes resources.

This validator checks:

  • Object is a valid map structure
  • Required metadata fields exist
  • Metadata fields have valid values

It subscribes to WebhookValidationRequest events and publishes WebhookValidationResponse events.

func NewBasicValidatorComponent

func NewBasicValidatorComponent(eventBus *busevents.EventBus, logger *slog.Logger) *BasicValidatorComponent

NewBasicValidatorComponent creates a new basic validator component.

func (*BasicValidatorComponent) Name

func (b *BasicValidatorComponent) Name() string

Name returns the unique identifier for this component. Implements the lifecycle.Component interface.

func (*BasicValidatorComponent) Start

Start begins the validator's event loop.

type Component

type Component struct {
	// contains filtered or unexported fields
}

Component is the webhook adapter component that manages webhook lifecycle.

It coordinates the pure webhook library server with the event-driven controller architecture.

func New

func New(eventBus *busevents.EventBus, logger *slog.Logger, config *Config, restMapper meta.RESTMapper, metrics MetricsRecorder) *Component

New creates a new webhook component.

Parameters:

  • eventBus: EventBus for publishing webhook events
  • logger: Structured logger
  • config: Component configuration (must include CertPEM and KeyPEM)
  • restMapper: RESTMapper for resolving resource kinds from GVR
  • metrics: Optional metrics recorder (can be nil)

Returns:

  • A new Component instance ready to be started

func (*Component) Name

func (c *Component) Name() string

Name returns the unique identifier for this component. Implements the lifecycle.Component interface.

func (*Component) RegisterValidator

func (c *Component) RegisterValidator(gvk string, validatorFunc webhook.ValidationFunc)

RegisterValidator registers a validation function for a specific resource type.

This should be called before Start() to register all validators.

Parameters:

  • gvk: Group/Version.Kind identifier (e.g., "networking.k8s.io/v1.Ingress", "v1.ConfigMap")
  • validatorFunc: The validation function to call for this resource type

func (*Component) Start

func (c *Component) Start(ctx context.Context) error

Start starts the webhook component.

This method: 1. Validates TLS certificates from configuration 2. Creates and starts the webhook HTTPS server 3. Publishes lifecycle events

The server continues running until the context is cancelled.

type Config

type Config struct {
	// Port is the HTTPS port for the webhook server.
	// Default: 9443
	Port int

	// Path is the URL path for validation requests.
	// Default: "/validate"
	Path string

	// CertPEM is the PEM-encoded TLS certificate.
	// Fetched from Kubernetes Secret via API.
	CertPEM []byte

	// KeyPEM is the PEM-encoded TLS private key.
	// Fetched from Kubernetes Secret via API.
	KeyPEM []byte

	// Rules defines which resources the webhook validates.
	// Used for registering validators by GVK.
	Rules []webhook.WebhookRule
}

Config configures the webhook component.

type MetricsRecorder

type MetricsRecorder interface {
	RecordWebhookRequest(gvk, result string, durationSeconds float64)
	RecordWebhookValidation(gvk, result string)
}

MetricsRecorder defines the interface for recording webhook metrics. This allows the component to work with or without metrics.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL