Files
kata-containers/tools/testing/kata-webhook/main.go
Lukáš Doktor ca91073d83 tools.kata-webhook: Add support for only-filter
sometimes it's hard to enumerate all blacklisted namespaces, lets add a
regular expression based only filter to allow specifying namespaces that
should be mutated.

Signed-off-by: Lukáš Doktor <ldoktor@redhat.com>
2025-11-11 15:21:15 +01:00

164 lines
4.7 KiB
Go

// Copyright (c) 2019 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
package main
import (
"context"
"flag"
"fmt"
"net/http"
"os"
"regexp"
"strings"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"github.com/sirupsen/logrus"
kwhhttp "github.com/slok/kubewebhook/v2/pkg/http"
kwhlogrus "github.com/slok/kubewebhook/v2/pkg/log/logrus"
kwhmodel "github.com/slok/kubewebhook/v2/pkg/model"
kwhmutating "github.com/slok/kubewebhook/v2/pkg/webhook/mutating"
)
func getRuntimeClass(runtimeClassKey, defaultRuntimeClass string) string {
if runtimeClass, ok := os.LookupEnv(runtimeClassKey); ok {
return runtimeClass
}
return defaultRuntimeClass
}
func annotatePodMutator(_ context.Context, ar *kwhmodel.AdmissionReview, obj metav1.Object) (*kwhmutating.MutatorResult, error) {
pod, ok := obj.(*corev1.Pod)
if !ok {
// If not a pod just continue the mutation chain (if there is one) and don't do anything
return &kwhmutating.MutatorResult{}, nil
}
// The Namespace is not always available in the pod Spec
// specially when operators create the pods. Hence access
// the Namespace in the actual request (vs the object)
// https://godoc.org/k8s.io/api/admission/v1beta1#AdmissionRequest
if whPolicy.nsBlacklist[ar.Namespace] {
fmt.Println("blacklisted namespace: ", ar.Namespace)
return &kwhmutating.MutatorResult{}, nil
}
// Check if we are only mutating pods in specific namespaces using the regular expression.
if whPolicy.nsOnlyRegexp != nil && !whPolicy.nsOnlyRegexp.MatchString(ar.Namespace) {
fmt.Println("namespace doesn't match re-only-namespaces: ", ar.Namespace)
return &kwhmutating.MutatorResult{}, nil
}
// We cannot support --net=host in Kata
// https://github.com/kata-containers/documentation/blob/master/Limitations.md#docker---nethost
if pod.Spec.HostNetwork {
fmt.Println("host network: ", pod.GetNamespace(), pod.GetName())
return &kwhmutating.MutatorResult{}, nil
}
if pod.GetNamespace() == "sonobuoy" {
fmt.Println("sonobuoy pods will not be changed to kata", pod.GetNamespace(), pod.GetName())
return &kwhmutating.MutatorResult{}, nil
}
if pod.Spec.RuntimeClassName != nil {
fmt.Println("explicit runtime: ", pod.GetNamespace(), pod.GetName(), pod.Spec.RuntimeClassName)
return &kwhmutating.MutatorResult{}, nil
}
// Mutate the pod
fmt.Println("setting runtime to kata: ", pod.GetNamespace(), pod.GetName())
runtimeClassEnvKey := "RUNTIME_CLASS"
kataRuntimeClassName := getRuntimeClass(runtimeClassEnvKey, "kata")
pod.Spec.RuntimeClassName = &kataRuntimeClassName
return &kwhmutating.MutatorResult{
MutatedObject: pod,
}, nil
}
type config struct {
certFile string
keyFile string
nsBlacklist string
nsOnlyRegexp string
}
type policy struct {
nsBlacklist map[string]bool
nsOnlyRegexp *regexp.Regexp
}
var whPolicy *policy
func initFlags() *config {
cfg := &config{}
fl := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
fl.StringVar(&cfg.certFile, "tls-cert-file", "", "TLS certificate file")
fl.StringVar(&cfg.keyFile, "tls-key-file", "", "TLS key file")
fl.StringVar(&cfg.nsBlacklist, "exclude-namespaces", "", "Comma separated namespace blacklist")
fl.StringVar(&cfg.nsOnlyRegexp, "only-namespaces-regexp", "", "Regexp namespace filter applied after --exclude-namespaces")
fl.Parse(os.Args[1:])
return cfg
}
func main() {
logrusLogEntry := logrus.NewEntry(logrus.New())
logrusLogEntry.Logger.SetLevel(logrus.DebugLevel)
logger := kwhlogrus.NewLogrus(logrusLogEntry)
cfg := initFlags()
whPolicy = &policy{}
whPolicy.nsBlacklist = make(map[string]bool)
if cfg.nsBlacklist != "" {
for _, s := range strings.Split(cfg.nsBlacklist, ",") {
whPolicy.nsBlacklist[s] = true
}
}
if cfg.nsOnlyRegexp != "" {
var err error
whPolicy.nsOnlyRegexp, err = regexp.Compile(cfg.nsOnlyRegexp)
if err != nil {
fmt.Fprintf(os.Stderr, "error compiling regular expression: %s", err)
os.Exit(1)
}
}
// Create our mutator
mt := kwhmutating.MutatorFunc(annotatePodMutator)
mcfg := kwhmutating.WebhookConfig{
ID: "podAnnotate",
Obj: &corev1.Pod{},
Mutator: mt,
Logger: logger,
}
wh, err := kwhmutating.NewWebhook(mcfg)
if err != nil {
fmt.Fprintf(os.Stderr, "error creating webhook: %s", err)
os.Exit(1)
}
// Get the handler for our webhook.
whHandler, err := kwhhttp.HandlerFor(kwhhttp.HandlerConfig{Webhook: wh, Logger: logger})
if err != nil {
fmt.Fprintf(os.Stderr, "error creating webhook handler: %s", err)
os.Exit(1)
}
port := ":8080"
logger.Infof("Listening on %s", port)
err = http.ListenAndServeTLS(port, cfg.certFile, cfg.keyFile, whHandler)
if err != nil {
fmt.Fprintf(os.Stderr, "error serving webhook: %s", err)
os.Exit(1)
}
}