From eaf2e71d7e0fdd97ea907605e9822e5d31b0812f Mon Sep 17 00:00:00 2001 From: Anurag Rajawat Date: Fri, 24 May 2024 16:37:27 +0530 Subject: [PATCH] feat(adapter): Add k8tls adapter Signed-off-by: Anurag Rajawat --- pkg/adapter/idpool/idpool.go | 12 +- pkg/adapter/k8s/client.go | 19 ++ pkg/adapter/nimbus-k8tls/.dockerignore | 1 + pkg/adapter/nimbus-k8tls/Dockerfile | 41 +++ pkg/adapter/nimbus-k8tls/Makefile | 34 ++ pkg/adapter/nimbus-k8tls/builder/builder.go | 91 ++++++ pkg/adapter/nimbus-k8tls/go.mod | 70 ++++ pkg/adapter/nimbus-k8tls/go.sum | 178 ++++++++++ pkg/adapter/nimbus-k8tls/main.go | 36 +++ pkg/adapter/nimbus-k8tls/manager/cronjob.go | 71 ++++ pkg/adapter/nimbus-k8tls/manager/job.go | 72 +++++ pkg/adapter/nimbus-k8tls/manager/manager.go | 341 ++++++++++++++++++++ pkg/adapter/nimbus-k8tls/watcher/watcher.go | 126 ++++++++ 13 files changed, 1088 insertions(+), 4 deletions(-) create mode 100644 pkg/adapter/nimbus-k8tls/.dockerignore create mode 100644 pkg/adapter/nimbus-k8tls/Dockerfile create mode 100644 pkg/adapter/nimbus-k8tls/Makefile create mode 100644 pkg/adapter/nimbus-k8tls/builder/builder.go create mode 100644 pkg/adapter/nimbus-k8tls/go.mod create mode 100644 pkg/adapter/nimbus-k8tls/go.sum create mode 100644 pkg/adapter/nimbus-k8tls/main.go create mode 100644 pkg/adapter/nimbus-k8tls/manager/cronjob.go create mode 100644 pkg/adapter/nimbus-k8tls/manager/job.go create mode 100644 pkg/adapter/nimbus-k8tls/manager/manager.go create mode 100644 pkg/adapter/nimbus-k8tls/watcher/watcher.go diff --git a/pkg/adapter/idpool/idpool.go b/pkg/adapter/idpool/idpool.go index 9f49805c..91ea8704 100644 --- a/pkg/adapter/idpool/idpool.go +++ b/pkg/adapter/idpool/idpool.go @@ -11,14 +11,11 @@ import ( const ( SwDeploymentTools = "swDeploymentTools" UnAuthorizedSaTokenAccess = "unAuthorizedSaTokenAccess" - UnAuthorizedNEFAccess = "unAuthorizedNEFAccess" - NFServiceDiscovery = "nfServiceDiscovery" DNSManipulation = "dnsManipulation" - NetPortExec = "netPortExec" - SysPathExec = "sysPathExec" EscapeToHost = "escapeToHost" DisallowChRoot = "disallowChRoot" DisallowCapabilities = "disallowCapabilities" + EnsureTLS = "ensureTLS" ) // KaIds are IDs supported by KubeArmor. @@ -45,6 +42,11 @@ var KyvIds = []string{ EscapeToHost, } +// k8tlsIds are IDs supported by k8tls. +var k8tlsIds = []string{ + EnsureTLS, +} + // IsIdSupportedBy determines whether a given ID is supported by a security engine. func IsIdSupportedBy(id, securityEngine string) bool { switch strings.ToLower(securityEngine) { @@ -54,6 +56,8 @@ func IsIdSupportedBy(id, securityEngine string) bool { return in(id, NetPolIDs) case "kyverno": return in(id, KyvIds) + case "k8tls": + return in(id, k8tlsIds) default: return false } diff --git a/pkg/adapter/k8s/client.go b/pkg/adapter/k8s/client.go index 008aeb89..d40fc35b 100644 --- a/pkg/adapter/k8s/client.go +++ b/pkg/adapter/k8s/client.go @@ -11,6 +11,7 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/dynamic" + "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" "sigs.k8s.io/controller-runtime/pkg/client" @@ -53,3 +54,21 @@ func NewDynamicClient() dynamic.Interface { } return clientSet } + +// NewOrDieStaticClient returns a new Kubernetes Clientset and panics if there is +// an error in the config. +func NewOrDieStaticClient() kubernetes.Interface { + config, err := rest.InClusterConfig() + if err != nil && errors.Is(err, rest.ErrNotInCluster) { + kubeconfig := filepath.Join(os.Getenv("HOME"), ".kube", "config") + config, err = clientcmd.BuildConfigFromFlags("", kubeconfig) + if err != nil { + panic(fmt.Sprintf("failed to load kubeconfig '%v', error: %v\n", kubeconfig, err)) + } + } + clientSet, err := kubernetes.NewForConfig(config) + if err != nil { + panic(err) + } + return clientSet +} diff --git a/pkg/adapter/nimbus-k8tls/.dockerignore b/pkg/adapter/nimbus-k8tls/.dockerignore new file mode 100644 index 00000000..6dd29b7f --- /dev/null +++ b/pkg/adapter/nimbus-k8tls/.dockerignore @@ -0,0 +1 @@ +bin/ \ No newline at end of file diff --git a/pkg/adapter/nimbus-k8tls/Dockerfile b/pkg/adapter/nimbus-k8tls/Dockerfile new file mode 100644 index 00000000..53569ab1 --- /dev/null +++ b/pkg/adapter/nimbus-k8tls/Dockerfile @@ -0,0 +1,41 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2023 Authors of Nimbus + +FROM golang:1.22 as builder +ARG TARGETOS +ARG TARGETARCH + +WORKDIR /nimbus + +# relative deps requried by the adapter +ADD api/ api/ +ADD pkg/ pkg/ +ADD go.mod go.mod +ADD go.sum go.sum + +ARG ADAPTER_DIR=pkg/adapter/nimbus-k8tls +WORKDIR /nimbus/$ADAPTER_DIR + +COPY $ADAPTER_DIR/go.mod go.mod +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer +RUN go mod download + +COPY $ADAPTER_DIR/manager manager +COPY $ADAPTER_DIR/builder builder +COPY $ADAPTER_DIR/watcher watcher +COPY $ADAPTER_DIR/main.go main.go + +# Build +# the GOARCH has not a default value to allow the binary be built according to the host where the command +# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO +# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, +# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -ldflags="-s" -o bin/nimbus-k8tls main.go + +FROM gcr.io/distroless/static:nonroot +WORKDIR / +COPY --from=builder /nimbus/pkg/adapter/nimbus-k8tls/bin/nimbus-k8tls . +USER 65532:65532 + +ENTRYPOINT ["/nimbus-k8tls"] diff --git a/pkg/adapter/nimbus-k8tls/Makefile b/pkg/adapter/nimbus-k8tls/Makefile new file mode 100644 index 00000000..84a38b4e --- /dev/null +++ b/pkg/adapter/nimbus-k8tls/Makefile @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2023 Authors of Nimbus + +# Image URL to use all building/pushing image targets +IMG ?= 5gsec/nimbus-k8tls +# Image Tag to use all building/pushing image targets +TAG ?= latest + +CONTAINER_TOOL ?= docker +BINARY ?= bin/nimbus-k8tls + +build: + @go build -ldflags="-s" -o ${BINARY} main.go + +run: build + @./${BINARY} + +.PHONY: docker-build +docker-build: + $(CONTAINER_TOOL) build -t ${IMG}:${TAG} --build-arg VERSION=${TAG} -f ./Dockerfile ../../../ + +.PHONY: docker-push +docker-push: + $(CONTAINER_TOOL) push ${IMG}:${TAG} + +PLATFORMS ?= linux/arm64,linux/amd64 +.PHONY: docker-buildx +docker-buildx: + sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross + - $(CONTAINER_TOOL) buildx create --name project-v3-builder + $(CONTAINER_TOOL) buildx use project-v3-builder + - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --build-arg VERSION=${TAG} --tag ${IMG}:${TAG} -f Dockerfile.cross ../../../ || { $(CONTAINER_TOOL) buildx rm project-v3-builder; rm Dockerfile.cross; exit 1; } + - $(CONTAINER_TOOL) buildx rm project-v3-builder + rm Dockerfile.cross diff --git a/pkg/adapter/nimbus-k8tls/builder/builder.go b/pkg/adapter/nimbus-k8tls/builder/builder.go new file mode 100644 index 00000000..dc39b5c2 --- /dev/null +++ b/pkg/adapter/nimbus-k8tls/builder/builder.go @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of Nimbus + +package builder + +import ( + "strings" + + "github.com/go-logr/logr" + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + intentv1 "github.com/5GSEC/nimbus/api/v1" + "github.com/5GSEC/nimbus/pkg/adapter/idpool" +) + +func BuildJobOrCronJob(logger logr.Logger, cwnp intentv1.ClusterNimbusPolicy) client.Object { + for _, nimbusRule := range cwnp.Spec.NimbusRules { + id := nimbusRule.ID + if idpool.IsIdSupportedBy(id, "k8tls") { + obj := buildJobOrCronJobFor(id, nimbusRule) + obj.SetName(cwnp.Name + "-" + strings.ToLower(id)) + obj.SetAnnotations(map[string]string{ + "app.kubernetes.io/managed-by": "nimbus-k8tls", + }) + obj.SetLabels(cwnp.Labels) + return obj + } + logger.Info("K8TLS adapter doesn't support this ID", "ID", id) + } + return nil +} + +func buildJobOrCronJobFor(id string, rule intentv1.NimbusRules) client.Object { + switch id { + case idpool.EnsureTLS: + return ensureTlsJobOrCronJob(rule) + default: + return nil + } +} + +func ensureTlsJobOrCronJob(rule intentv1.NimbusRules) client.Object { + schedule, keyExists := rule.Rule.Params["schedule"] + if keyExists { + return cronJobForEnsureTls(schedule[0]) + } + return jobForEnsureTls() +} + +var k8tlsPodSpec = corev1.PodSpec{ + RestartPolicy: corev1.RestartPolicyNever, + Containers: []corev1.Container{ + { + Name: "k8tls", + Image: "kubearmor/k8tls:latest", + Command: []string{"./k8s_tlsscan"}, + ImagePullPolicy: corev1.PullAlways, + }, + }, +} + +func jobForEnsureTls() client.Object { + backOffLimit := int32(5) + return &batchv1.Job{ + Spec: batchv1.JobSpec{ + BackoffLimit: &backOffLimit, + Template: corev1.PodTemplateSpec{ + Spec: k8tlsPodSpec, + }, + }, + } +} + +func cronJobForEnsureTls(schedule string) client.Object { + backOffLimit := int32(5) + return &batchv1.CronJob{ + Spec: batchv1.CronJobSpec{ + Schedule: schedule, + JobTemplate: batchv1.JobTemplateSpec{ + Spec: batchv1.JobSpec{ + BackoffLimit: &backOffLimit, + Template: corev1.PodTemplateSpec{ + Spec: k8tlsPodSpec, + }, + }, + }, + }, + } +} diff --git a/pkg/adapter/nimbus-k8tls/go.mod b/pkg/adapter/nimbus-k8tls/go.mod new file mode 100644 index 00000000..65116816 --- /dev/null +++ b/pkg/adapter/nimbus-k8tls/go.mod @@ -0,0 +1,70 @@ +module github.com/5GSEC/nimbus/pkg/adapter/nimbus-k8tls + +go 1.22.0 + +toolchain go1.22.3 + +replace github.com/5GSEC/nimbus => ../../../../nimbus + +require ( + github.com/5GSEC/nimbus v0.0.0-20240313065715-b91563b0ccd3 + github.com/go-logr/logr v1.4.2 + k8s.io/api v0.30.1 + k8s.io/apimachinery v0.30.1 + k8s.io/client-go v0.30.1 + sigs.k8s.io/controller-runtime v0.18.2 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/emicklei/go-restful/v3 v3.12.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/zapr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/imdario/mergo v0.3.16 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.53.0 // indirect + github.com/prometheus/procfs v0.15.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/oauth2 v0.20.0 // indirect + golang.org/x/sys v0.20.0 // indirect + golang.org/x/term v0.20.0 // indirect + golang.org/x/text v0.15.0 // indirect + golang.org/x/time v0.5.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/protobuf v1.34.1 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/apiextensions-apiserver v0.30.1 // indirect + k8s.io/klog/v2 v2.120.1 // indirect + k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a // indirect + k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) diff --git a/pkg/adapter/nimbus-k8tls/go.sum b/pkg/adapter/nimbus-k8tls/go.sum new file mode 100644 index 00000000..1dd0a0b1 --- /dev/null +++ b/pkg/adapter/nimbus-k8tls/go.sum @@ -0,0 +1,178 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk= +github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= +github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= +github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= +github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= +github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49/go.mod h1:BkkQ4L1KS1xMt2aWSPStnn55ChGC0DPOn2FQYj+f25M= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6 h1:k7nVchz72niMH6YLQNvHSdIE7iqsQxK1P41mySCvssg= +github.com/google/pprof v0.0.0-20240424215950-a892ee059fd6/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.17.2 h1:7eMhcy3GimbsA3hEnVKdw/PQM9XN9krpKVXsZdph0/g= +github.com/onsi/ginkgo/v2 v2.17.2/go.mod h1:nP2DPOQoNsQmsVyv5rDA8JkXQoCs6goXIvr/PRJ1eCc= +github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= +github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= +github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= +github.com/prometheus/procfs v0.15.0 h1:A82kmvXJq2jTu5YUhSGNlYoxh85zLnKgPz4bMZgI5Ek= +github.com/prometheus/procfs v0.15.0/go.mod h1:Y0RJ/Y5g5wJpkTisOtqwDSo4HwhGmLB4VQSw2sQJLHk= +github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= +golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.21.0 h1:qc0xYgIbsSDt9EyWz05J5wfa7LOVW0YTLOXrqdLAWIw= +golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.30.1 h1:kCm/6mADMdbAxmIh0LBjS54nQBE+U4KmbCfIkF5CpJY= +k8s.io/api v0.30.1/go.mod h1:ddbN2C0+0DIiPntan/bye3SW3PdwLa11/0yqwvuRrJM= +k8s.io/apiextensions-apiserver v0.30.1 h1:4fAJZ9985BmpJG6PkoxVRpXv9vmPUOVzl614xarePws= +k8s.io/apiextensions-apiserver v0.30.1/go.mod h1:R4GuSrlhgq43oRY9sF2IToFh7PVlF1JjfWdoG3pixk4= +k8s.io/apimachinery v0.30.1 h1:ZQStsEfo4n65yAdlGTfP/uSHMQSoYzU/oeEbkmF7P2U= +k8s.io/apimachinery v0.30.1/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc= +k8s.io/client-go v0.30.1 h1:uC/Ir6A3R46wdkgCV3vbLyNOYyCJ8oZnjtJGKfytl/Q= +k8s.io/client-go v0.30.1/go.mod h1:wrAqLNs2trwiCH/wxxmT/x3hKVH9PuV0GGW0oDoHVqc= +k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= +k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a h1:zD1uj3Jf+mD4zmA7W+goE5TxDkI7OGJjBNBzq5fJtLA= +k8s.io/kube-openapi v0.0.0-20240521193020-835d969ad83a/go.mod h1:UxDHUPsUwTOOxSU+oXURfFBcAS6JwiRXTYqYwfuGowc= +k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak= +k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.18.2 h1:RqVW6Kpeaji67CY5nPEfRz6ZfFMk0lWQlNrLqlNpx+Q= +sigs.k8s.io/controller-runtime v0.18.2/go.mod h1:tuAt1+wbVsXIT8lPtk5RURxqAnq7xkpv2Mhttslg7Hw= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/pkg/adapter/nimbus-k8tls/main.go b/pkg/adapter/nimbus-k8tls/main.go new file mode 100644 index 00000000..ba7d46c7 --- /dev/null +++ b/pkg/adapter/nimbus-k8tls/main.go @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of Nimbus + +package main + +import ( + "context" + "os" + "os/signal" + "syscall" + + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + "github.com/5GSEC/nimbus/pkg/adapter/nimbus-k8tls/manager" +) + +func main() { + ctrl.SetLogger(zap.New()) + logger := ctrl.Log + + ctx, cancelFunc := context.WithCancel(context.Background()) + ctrl.LoggerInto(ctx, logger) + + go func() { + termChan := make(chan os.Signal) + signal.Notify(termChan, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT) + <-termChan + logger.Info("Shutdown signal received, waiting for all workers to finish") + cancelFunc() + logger.Info("All workers finished, shutting down") + }() + + logger.Info("K8TLS adapter started") + manager.Run(ctx) +} diff --git a/pkg/adapter/nimbus-k8tls/manager/cronjob.go b/pkg/adapter/nimbus-k8tls/manager/cronjob.go new file mode 100644 index 00000000..cea58d46 --- /dev/null +++ b/pkg/adapter/nimbus-k8tls/manager/cronjob.go @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of Nimbus + +package manager + +import ( + "context" + + "github.com/go-logr/logr" + batchv1 "k8s.io/api/batch/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + + intentv1 "github.com/5GSEC/nimbus/api/v1" + adapterutil "github.com/5GSEC/nimbus/pkg/adapter/util" +) + +func createOrUpdateCronJob(ctx context.Context, logger logr.Logger, cwnp intentv1.ClusterNimbusPolicy, cronJob *batchv1.CronJob) { + cronJob.Spec.JobTemplate.Spec.Template.Spec.ServiceAccountName = NamespaceName + if err := ctrl.SetControllerReference(&cwnp, cronJob, scheme); err != nil { + logger.Error(err, "failed to set OwnerReference on Kubernetes CronJob", "CronJob.Name", cronJob.Name) + return + } + + var existingCronJob batchv1.CronJob + err := k8sClient.Get(ctx, types.NamespacedName{Name: cronJob.Name, Namespace: cronJob.Namespace}, &existingCronJob) + if err != nil && !errors.IsNotFound(err) { + logger.Error(err, "failed to get Kubernetes CronJob", "CronJob.Name", cronJob.Name, "CronJob.Namespace", cronJob.Namespace) + return + } + + if err != nil { + if errors.IsNotFound(err) { + if err = k8sClient.Create(ctx, cronJob); err != nil { + logger.Error(err, "failed to create Kubernetes CronJob", "CronJob.Name", cronJob.Name, "CronJob.Namespace", cronJob.Namespace) + return + } + logger.Info("created Kubernetes CronJob", "CronJob.Name", cronJob.Name, "CronJob.Namespace", cronJob.Namespace) + } + } else { + cronJob.ResourceVersion = existingCronJob.ResourceVersion + cronJob.Labels = existingCronJob.Labels + + if err = k8sClient.Update(ctx, cronJob); err != nil { + logger.Error(err, "failed to configure existing Kubernetes CronJob", "CronJob.Name", cronJob.Name, "CronJob.Namespace", cronJob.Namespace) + return + } + logger.Info("configured Kubernetes CronJob", "CronJob.Name", cronJob.Name, "CronJob.Namespace", cronJob.Namespace) + } + + if err = adapterutil.UpdateCnpStatus(ctx, k8sClient, "CronJob/"+cronJob.Name, cwnp.Name, false); err != nil { + logger.Error(err, "failed to update ClusterNimbusPolicy status") + } +} + +func deleteDanglingCronJobs(ctx context.Context, logger logr.Logger, cwnpName string, cronJobsToDelete map[string]batchv1.CronJob) { + for cronJobName := range cronJobsToDelete { + cronJob := cronJobsToDelete[cronJobName] + if err := k8sClient.Delete(ctx, &cronJob); err != nil { + logger.Error(err, "failed to delete Kubernetes CronJob", "CronJob.Name", cronJobName, "CronJob.Namespace", cronJob.Namespace) + continue + } + + if err := adapterutil.UpdateNpStatus(ctx, k8sClient, "CronJob/"+cronJobName, cwnpName, cronJob.Namespace, true); err != nil { + logger.Error(err, "failed to update ClusterNimbusPolicy status") + } + logger.Info("Dangling Kubernetes CronJob deleted", "CronJobJob.Name", cronJob.Name, "CronJob.Namespace", cronJob.Namespace) + } + +} diff --git a/pkg/adapter/nimbus-k8tls/manager/job.go b/pkg/adapter/nimbus-k8tls/manager/job.go new file mode 100644 index 00000000..e2062496 --- /dev/null +++ b/pkg/adapter/nimbus-k8tls/manager/job.go @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of Nimbus + +package manager + +import ( + "context" + + "github.com/go-logr/logr" + batchv1 "k8s.io/api/batch/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + + intentv1 "github.com/5GSEC/nimbus/api/v1" + adapterutil "github.com/5GSEC/nimbus/pkg/adapter/util" +) + +func createOrUpdateJob(ctx context.Context, logger logr.Logger, cwnp intentv1.ClusterNimbusPolicy, job *batchv1.Job) { + job.Spec.Template.Spec.ServiceAccountName = NamespaceName + if err := ctrl.SetControllerReference(&cwnp, job, scheme); err != nil { + logger.Error(err, "failed to set OwnerReference on Kubernetes Job", "Name", job.Name) + return + } + + var existingJob batchv1.Job + err := k8sClient.Get(ctx, types.NamespacedName{Name: job.Name, Namespace: job.Namespace}, &existingJob) + if err != nil && !errors.IsNotFound(err) { + logger.Error(err, "failed to get Kubernetes Job", "Job.Name", job.Name, "Job.Namespace", NamespaceName) + return + } + + if err != nil { + if errors.IsNotFound(err) { + if err = k8sClient.Create(ctx, job); err != nil { + logger.Error(err, "failed to create Kubernetes Job", "Job.Name", job.Name, "Job.Namespace", NamespaceName) + return + } + logger.Info("created Kubernetes Job", "Job.Name", job.Name, "Job.Namespace", NamespaceName) + } + } else { + job.ResourceVersion = existingJob.ResourceVersion + job.Labels = existingJob.Labels + job.Spec.Selector = existingJob.Spec.Selector + job.Spec.Template.Labels = existingJob.Spec.Template.Labels + + if err = k8sClient.Update(ctx, job); err != nil { + logger.Error(err, "failed to configure existing Kubernetes Job", "Job.Name", job.Name, "Job.Namespace", NamespaceName) + return + } + logger.Info("configured Kubernetes Job", "Job.Name", job.Name, "Job.Namespace", NamespaceName) + } + + if err = adapterutil.UpdateCnpStatus(ctx, k8sClient, "Job/"+job.Name, cwnp.Name, false); err != nil { + logger.Error(err, "failed to update ClusterNimbusPolicy status") + } +} + +func deleteDanglingJobs(ctx context.Context, logger logr.Logger, cwnpName string, jobsToDelete map[string]batchv1.Job) { + for jobName := range jobsToDelete { + job := jobsToDelete[jobName] + if err := k8sClient.Delete(ctx, &job); err != nil { + logger.Error(err, "failed to delete Kubernetes Job", "Job.Name", jobName, "Job.Namespace", NamespaceName) + continue + } + + if err := adapterutil.UpdateNpStatus(ctx, k8sClient, "Job/"+jobName, cwnpName, NamespaceName, true); err != nil { + logger.Error(err, "failed to update ClusterNimbusPolicy status") + } + logger.Info("Dangling Kubernetes Job deleted", "Job.Name", job.Name, "Job.Namespace", NamespaceName) + } +} diff --git a/pkg/adapter/nimbus-k8tls/manager/manager.go b/pkg/adapter/nimbus-k8tls/manager/manager.go new file mode 100644 index 00000000..dc97fb9b --- /dev/null +++ b/pkg/adapter/nimbus-k8tls/manager/manager.go @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of Nimbus + +package manager + +import ( + "context" + "fmt" + "strings" + + "github.com/go-logr/logr" + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + intentv1 "github.com/5GSEC/nimbus/api/v1" + "github.com/5GSEC/nimbus/pkg/adapter/common" + "github.com/5GSEC/nimbus/pkg/adapter/k8s" + "github.com/5GSEC/nimbus/pkg/adapter/nimbus-k8tls/builder" + "github.com/5GSEC/nimbus/pkg/adapter/nimbus-k8tls/watcher" + adapterutil "github.com/5GSEC/nimbus/pkg/adapter/util" + globalwatcher "github.com/5GSEC/nimbus/pkg/adapter/watcher" +) + +var ( + scheme = runtime.NewScheme() + k8sClient client.Client + NamespaceName = "nimbus-k8tls" +) + +func init() { + utilruntime.Must(corev1.AddToScheme(scheme)) + utilruntime.Must(batchv1.AddToScheme(scheme)) + utilruntime.Must(rbacv1.AddToScheme(scheme)) + utilruntime.Must(intentv1.AddToScheme(scheme)) + k8sClient = k8s.NewOrDie(scheme) +} + +func Run(ctx context.Context) { + cwnpCh := make(chan string) + deletedcwnpCh := make(chan string) + go globalwatcher.WatchClusterNimbusPolicies(ctx, cwnpCh, deletedcwnpCh) + + updateJobCh := make(chan common.Request) + deletedJobCh := make(chan common.Request) + go watcher.WatchJobs(ctx, updateJobCh, deletedJobCh) + + updateCronJobCh := make(chan common.Request) + deletedCronJobCh := make(chan common.Request) + go watcher.WatchCronJobs(ctx, updateCronJobCh, deletedCronJobCh) + + for { + select { + case <-ctx.Done(): + close(cwnpCh) + close(deletedcwnpCh) + + close(updateJobCh) + close(deletedJobCh) + + close(updateCronJobCh) + close(deletedCronJobCh) + return + + case createdCwnp := <-cwnpCh: + createOrUpdateJobOrCronJob(ctx, createdCwnp) + case deletedCwnp := <-deletedcwnpCh: + deleteJobOrCronJob(ctx, deletedCwnp) + + case updatedJob := <-updateJobCh: + reconcileJobOrCronJob(ctx, updatedJob.Name) + case deletedJob := <-deletedJobCh: + reconcileJobOrCronJob(ctx, deletedJob.Name) + + case updatedCronJob := <-updateCronJobCh: + reconcileJobOrCronJob(ctx, updatedCronJob.Name) + case deletedCronJob := <-deletedCronJobCh: + reconcileJobOrCronJob(ctx, deletedCronJob.Name) + } + } +} + +func reconcileJobOrCronJob(ctx context.Context, name string) { + logger := log.FromContext(ctx) + cwnpName := adapterutil.ExtractClusterNpName(name) + var cwnp intentv1.ClusterNimbusPolicy + err := k8sClient.Get(ctx, types.NamespacedName{Name: cwnpName}, &cwnp) + if err != nil { + if !errors.IsNotFound(err) { + logger.Error(err, "failed to get ClusterNimbusPolicy", "ClusterNimbusPolicy.Name", cwnpName) + } + return + } + createOrUpdateJobOrCronJob(ctx, cwnpName) +} + +func createOrUpdateJobOrCronJob(ctx context.Context, cwnpName string) { + logger := log.FromContext(ctx) + var cwnp intentv1.ClusterNimbusPolicy + if err := k8sClient.Get(ctx, types.NamespacedName{Name: cwnpName}, &cwnp); err != nil { + logger.Error(err, "failed to get ClusterNimbusPolicy", "ClusterNimbusPolicy.Name", cwnpName) + return + } + + if adapterutil.IsOrphan(cwnp.GetOwnerReferences(), "ClusterSecurityIntentBinding") { + logger.V(4).Info("Ignoring orphan ClusterNimbusPolicy", "ClusterNimbusPolicy.Name", cwnpName) + return + } + + deleteDanglingJobOrCj(ctx, logger, cwnp) + obj := builder.BuildJobOrCronJob(logger, cwnp) + obj.SetNamespace(NamespaceName) + + if obj != nil { + if err := setupK8tlsEnv(ctx, cwnp, scheme, k8sClient); err != nil { + logger.Error(err, "failed to setup k8tls env") + return + } + + switch obj.(type) { + case *batchv1.Job: + createOrUpdateJob(ctx, logger, cwnp, obj.(*batchv1.Job)) + case *batchv1.CronJob: + createOrUpdateCronJob(ctx, logger, cwnp, obj.(*batchv1.CronJob)) + } + } +} + +func deleteJobOrCronJob(ctx context.Context, cwnpName string) { + logger := log.FromContext(ctx) + + var existingJobs batchv1.JobList + if err := k8sClient.List(ctx, &existingJobs, &client.ListOptions{Namespace: NamespaceName}); err != nil { + logger.Error(err, "failed to list Kubernetes Job") + return + } + if len(existingJobs.Items) > 0 { + for _, job := range existingJobs.Items { + logger.Info( + "Kubernetes Job deleted due to ClusterNimbusPolicy deletion", + "Job.Name", job.Name, "Job.Namespace", NamespaceName, + "ClusterNimbusPolicy.Name", cwnpName, "ClusterNimbusPolicy.Namespace", NamespaceName, + ) + } + return + } + + var existingCronJobs batchv1.CronJobList + if err := k8sClient.List(ctx, &existingCronJobs, &client.ListOptions{Namespace: NamespaceName}); err != nil { + logger.Error(err, "failed to list Kubernetes CronJob") + return + } + for _, cronJob := range existingCronJobs.Items { + logger.Info( + "Kubernetes CronJob deleted due to ClusterNimbusPolicy deletion", + "CronJob.Name", cronJob.Name, "CronJob.Namespace", cronJob.Namespace, + "ClusterNimbusPolicy.Name", cwnpName) + } +} + +func deleteDanglingJobOrCj(ctx context.Context, logger logr.Logger, cwnp intentv1.ClusterNimbusPolicy) { + var existingJobs batchv1.JobList + if err := k8sClient.List(ctx, &existingJobs, &client.ListOptions{Namespace: NamespaceName}); err != nil { + logger.Error(err, "failed to list Kubernetes Job for cleanup") + return + } + + var jobsOwnedByCwnp []batchv1.Job + for _, job := range existingJobs.Items { + for _, ownerRef := range job.OwnerReferences { + if ownerRef.Name == cwnp.Name && ownerRef.UID == cwnp.UID { + jobsOwnedByCwnp = append(jobsOwnedByCwnp, job) + break + } + } + } + if len(jobsOwnedByCwnp) > 0 { + jobsToDelete := make(map[string]batchv1.Job) + + for _, job := range jobsOwnedByCwnp { + jobsToDelete[job.Name] = job + } + + for _, nimbusRule := range cwnp.Spec.NimbusRules { + jobName := cwnp.Name + "-" + strings.ToLower(nimbusRule.ID) + delete(jobsToDelete, jobName) + } + + deleteDanglingJobs(ctx, logger, cwnp.Name, jobsToDelete) + return + } + + var existingCronJobs batchv1.CronJobList + if err := k8sClient.List(ctx, &existingCronJobs, &client.ListOptions{Namespace: NamespaceName}); err != nil { + logger.Error(err, "failed to list Kubernetes CronJob for cleanup") + return + } + + var cronJobsOwnedByCwnp []batchv1.CronJob + for _, cronJob := range existingCronJobs.Items { + for _, ownerRef := range cronJob.OwnerReferences { + if ownerRef.Name == cwnp.Name && ownerRef.UID == cwnp.UID { + cronJobsOwnedByCwnp = append(cronJobsOwnedByCwnp, cronJob) + break + } + } + } + if len(cronJobsOwnedByCwnp) > 0 { + cronJobsToDelete := make(map[string]batchv1.CronJob) + for _, cronJob := range cronJobsOwnedByCwnp { + cronJobsToDelete[cronJob.Name] = cronJob + } + + for _, nimbusRule := range cwnp.Spec.NimbusRules { + cronJobName := cwnp.Name + "-" + strings.ToLower(nimbusRule.ID) + delete(cronJobsToDelete, cronJobName) + } + deleteDanglingCronJobs(ctx, logger, cwnp.Name, cronJobsToDelete) + } +} + +func setupK8tlsEnv(ctx context.Context, cwnp intentv1.ClusterNimbusPolicy, scheme *runtime.Scheme, k8sClient client.Client) error { + logger := log.FromContext(ctx) + + ns := &corev1.Namespace{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "Namespace", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: NamespaceName, + Labels: cwnp.Labels, + Annotations: map[string]string{ + "app.kubernetes.io/managed-by": "nimbus-k8tls", + }, + }, + } + objectMeta := metav1.ObjectMeta{ + Name: ns.Name, + Namespace: ns.Name, + Labels: ns.Labels, + Annotations: ns.Annotations, + } + + sa := &corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1.SchemeGroupVersion.String(), + Kind: "ServiceAccount", + }, + ObjectMeta: objectMeta, + } + + clusterRole := &rbacv1.ClusterRole{ + TypeMeta: metav1.TypeMeta{ + APIVersion: rbacv1.SchemeGroupVersion.String(), + Kind: "ClusterRole", + }, + ObjectMeta: objectMeta, + Rules: []rbacv1.PolicyRule{ + { + Verbs: []string{"get", "list"}, + APIGroups: []string{""}, + Resources: []string{"services"}, + }, + }, + } + + clusterRoleBinding := &rbacv1.ClusterRoleBinding{ + TypeMeta: metav1.TypeMeta{ + APIVersion: rbacv1.SchemeGroupVersion.String(), + Kind: "ClusterRoleBinding", + }, + ObjectMeta: objectMeta, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + APIGroup: "", + Name: sa.Name, + Namespace: sa.Namespace, + }, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: clusterRole.Name, + }, + } + + objs := []client.Object{ns, sa, clusterRole, clusterRoleBinding} + for idx := range objs { + objToCreate := objs[idx] + if err := ctrl.SetControllerReference(&cwnp, objToCreate, scheme); err != nil { + return err + } + + var existingObj client.Object + + // Set the type of object, otherwise existingObj will always remain nil. + switch objToCreate.(type) { + case *corev1.Namespace: + existingObj = &corev1.Namespace{} + case *corev1.ServiceAccount: + existingObj = &corev1.ServiceAccount{} + case *rbacv1.ClusterRole: + existingObj = &rbacv1.ClusterRole{} + case *rbacv1.ClusterRoleBinding: + existingObj = &rbacv1.ClusterRoleBinding{} + } + + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(objToCreate), existingObj) + if err != nil && !errors.IsNotFound(err) { + return err + } + + objKind := strings.ToLower(objToCreate.GetObjectKind().GroupVersionKind().Kind) + if err != nil { + if errors.IsNotFound(err) { + if err := k8sClient.Create(ctx, objToCreate); err != nil { + return err + } + logger.Info(fmt.Sprintf("created %s/%s", objKind, objToCreate.GetName())) + } + } else { + objToCreate.SetResourceVersion(existingObj.GetResourceVersion()) + if err := k8sClient.Update(ctx, objToCreate); err != nil { + return err + } + logger.Info(fmt.Sprintf("configured %s/%s", objKind, objToCreate.GetName())) + } + } + + return nil +} diff --git a/pkg/adapter/nimbus-k8tls/watcher/watcher.go b/pkg/adapter/nimbus-k8tls/watcher/watcher.go new file mode 100644 index 00000000..b12cabef --- /dev/null +++ b/pkg/adapter/nimbus-k8tls/watcher/watcher.go @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of Nimbus + +package watcher + +import ( + "context" + "time" + + batchv1 "k8s.io/api/batch/v1" + "k8s.io/client-go/informers" + "k8s.io/client-go/tools/cache" + "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/5GSEC/nimbus/pkg/adapter/common" + "github.com/5GSEC/nimbus/pkg/adapter/k8s" + adapterutil "github.com/5GSEC/nimbus/pkg/adapter/util" +) + +var ( + factory informers.SharedInformerFactory +) + +func init() { + factory = informers.NewSharedInformerFactory(k8s.NewOrDieStaticClient(), time.Minute) +} + +func jobInformer() cache.SharedIndexInformer { + return factory.Batch().V1().Jobs().Informer() +} + +func cronJobInformer() cache.SharedIndexInformer { + return factory.Batch().V1().CronJobs().Informer() +} + +// WatchJobs watches for update and delete events for Kubernetes Jobs owned by +// ClusterNimbusPolicy and put their info on corresponding channels. +func WatchJobs(ctx context.Context, updatedJobCh, deletedJobCh chan common.Request) { + logger := log.FromContext(ctx) + informer := jobInformer() + handlers := cache.ResourceEventHandlerFuncs{ + UpdateFunc: func(oldObj, newObj interface{}) { + oldJob := oldObj.(*batchv1.Job) + newJob := newObj.(*batchv1.Job) + + if adapterutil.IsOrphan(newJob.GetOwnerReferences(), "ClusterNimbusPolicy") { + logger.V(4).Info("Ignoring orphan Job", "Job.Name", newJob.GetName(), "Job.Namespace", newJob.GetNamespace(), "Operation", "Update") + return + } + + if oldJob.GetGeneration() == newJob.GetGeneration() { + return + } + + jobNamespacedName := common.Request{ + Name: newJob.GetName(), + Namespace: newJob.GetNamespace(), + } + updatedJobCh <- jobNamespacedName + }, + DeleteFunc: func(obj interface{}) { + job := obj.(*batchv1.Job) + if adapterutil.IsOrphan(job.GetOwnerReferences(), "ClusterNimbusPolicy") { + logger.V(4).Info("Ignoring orphan Job", "Job.Name", job.GetName(), "Job.Namespace", job.GetNamespace(), "Operation", "Delete") + return + } + jobNamespacedName := common.Request{ + Name: job.GetName(), + Namespace: job.GetNamespace(), + } + deletedJobCh <- jobNamespacedName + }, + } + if _, err := informer.AddEventHandler(handlers); err != nil { + logger.Error(err, "failed to add event handler") + return + } + logger.Info("Kubernetes Job watcher started") + informer.Run(ctx.Done()) +} + +// WatchCronJobs watches for update and delete events for Kubernetes CronJobs +// owned by ClusterNimbusPolicy and put their info on corresponding channels. +func WatchCronJobs(ctx context.Context, updatedCronJobCh, deletedCronJobCh chan common.Request) { + logger := log.FromContext(ctx) + informer := cronJobInformer() + handlers := cache.ResourceEventHandlerFuncs{ + UpdateFunc: func(oldObj, newObj interface{}) { + oldCronJob := oldObj.(*batchv1.CronJob) + newCronJob := newObj.(*batchv1.CronJob) + + if adapterutil.IsOrphan(newCronJob.GetOwnerReferences(), "ClusterNimbusPolicy") { + logger.V(4).Info("Ignoring orphan CronJob", "CronJob.Name", oldCronJob.GetName(), "CronJob.Namespace", oldCronJob.GetNamespace(), "Operation", "Update") + return + } + + if oldCronJob.GetGeneration() == newCronJob.GetGeneration() { + return + } + + cronJobNamespacedName := common.Request{ + Name: newCronJob.GetName(), + Namespace: newCronJob.GetNamespace(), + } + updatedCronJobCh <- cronJobNamespacedName + }, + DeleteFunc: func(obj interface{}) { + cronJob := obj.(*batchv1.CronJob) + if adapterutil.IsOrphan(cronJob.GetOwnerReferences(), "ClusterNimbusPolicy") { + logger.V(4).Info("Ignoring orphan CronJob", "CronJob.Name", cronJob.GetName(), "CronJob.Namespace", cronJob.GetNamespace(), "Operation", "Delete") + return + } + cronJobNamespacedName := common.Request{ + Name: cronJob.GetName(), + Namespace: cronJob.GetNamespace(), + } + deletedCronJobCh <- cronJobNamespacedName + }, + } + if _, err := informer.AddEventHandler(handlers); err != nil { + logger.Error(err, "failed to add event handler") + return + } + logger.Info("Kubernetes CronJob watcher started") + informer.Run(ctx.Done()) +}