Skip to content

Go framework to create Kubernetes mutating and validating webhooks

License

Notifications You must be signed in to change notification settings

slok/kubewebhook

Repository files navigation

kubewebhook

kubewebhook

CI Go Report Card GoDoc Apache 2 licensed GitHub release (latest SemVer) Kubernetes release

Kubewebhook is a small Go framework to create external admission webhooks for Kubernetes.

With Kubewebhook you can make validating and mutating webhooks in any version, fast, easy, and focusing mainly on the domain logic of the webhook itself.

Features

  • Ready for mutating and validating webhook kinds.
  • Abstracts webhook versioning (compatible with v1beta1 and v1).
  • Resource inference (compatible with CRDs and fallbacks to Unstructured).
  • Easy and testable API.
  • Simple, extensible and flexible.
  • Multiple webhooks on the same server.
  • Webhook metrics (RED) for Prometheus with Grafana dashboard included.
  • Webhook tracing with Opentelemetry support.
  • Supports warnings.

Getting started

Use github.com/slok/kubewebhook/v2 to import Kubewebhook v2.

func run() error {
    logger := &kwhlog.Std{Debug: true}

    // Create our mutator
    mt := kwhmutating.MutatorFunc(func(_ context.Context, _ *kwhmodel.AdmissionReview, obj metav1.Object) (*kwhmutating.MutatorResult, error) {
        pod, ok := obj.(*corev1.Pod)
        if !ok {
            return &kwhmutating.MutatorResult{}, nil
        }

        // Mutate our object with the required annotations.
        if pod.Annotations == nil {
            pod.Annotations = make(map[string]string)
        }
        pod.Annotations["mutated"] = "true"
        pod.Annotations["mutator"] = "pod-annotate"

        return &kwhmutating.MutatorResult{MutatedObject: pod}, nil
    })

    // Create webhook.
    wh, err := kwhmutating.NewWebhook(kwhmutating.WebhookConfig{
        ID:      "pod-annotate",
        Mutator: mt,
        Logger:  logger,
    })
    if err != nil {
        return fmt.Errorf("error creating webhook: %w", err)
    }

    // Get HTTP handler from webhook.
    whHandler, err := kwhhttp.HandlerFor(kwhhttp.HandlerConfig{Webhook: wh, Logger: logger})
    if err != nil {
        return fmt.Errorf("error creating webhook handler: %w", err)
    }

    // Serve.
    logger.Infof("Listening on :8080")
    err = http.ListenAndServeTLS(":8080", cfg.certFile, cfg.keyFile, whHandler)
    if err != nil {
        return fmt.Errorf("error serving webhook: %w", err)
    }

    return nil

You can get more examples in here

Production ready example

This repository is a production ready webhook app: https://github.com/slok/k8s-webhook-example

It shows, different webhook use cases, app structure, testing domain logic, kubewebhook use case, how to deploy...

Static and dynamic webhooks

We have 2 kinds of webhooks:

  • Static: Common one, is a single resource type webhook.
  • Dynamic: Used when the same webhook act on multiple types, unknown types and/or is used for generic stuff (e.g labels).
    • To use this kind of webhook, don't set the type on the configuration or set to nil.
    • If a request for an unknown type is not known by the webhook libraries, it will fallback to runtime.Unstructured object type.
    • Very useful to manipulate multiple resources on the same webhook (e.g Deployments, Statefulsets).
    • CRDs are unknown types so they will fallback to runtime.Unstructured`.
    • If using CRDs, better use Static webhooks.
    • Very useful to maniputale any metadata based validation or mutations (e.g Labels, annotations...)

Compatibility matrix

To know the validated compatibility, check the integration tests on CI.

Kubewebhook Kubernetes Admission reviews Dynamic webhooks OpenTelemetry tracing
v2.7 1.31, 1.30, 1.29, 1.28 v1beta1, v1
v2.6 1.29, 1.28, 1.27, 1.26 v1beta1, v1
v2.5 1.25 v1beta1, v1
v2.4 1.24 v1beta1, v1
v2.3 1.23 v1beta1, v1
v2.2 1.22 v1beta1, v1
v2.1 1.21 v1beta1, v1
v2.0 1.20 v1beta1, v1
v0.11 1.19 v1beta1
v0.10 1.18 v1beta1
v0.9 1.18 v1beta1
v0.8 1.17 v1beta1
v0.7 1.16 v1beta1
v0.6 1.15 v1beta1
v0.5 1.14 v1beta1
v0.4 1.13 v1beta1
v0.3 1.12 v1beta1
v0.2 1.11 v1beta1
v0.2 1.10 v1beta1

Documentation

You can access here.