From 94c5227d2577a443f3e662bb9ea4a854c4e4ce3f Mon Sep 17 00:00:00 2001 From: Achref ben saad Date: Mon, 26 Sep 2022 15:18:12 +0000 Subject: [PATCH] feat: add-kubearmor-operator Signed-off-by: Achref ben saad --- .github/workflows/ci-operator-release.yaml | 43 ++ .github/workflows/ci-test-ginkgo.yml | 24 +- .github/workflows/ci-test-operator.yaml | 31 + .gitignore | 4 +- KubeArmor/common/common.go | 6 +- KubeArmor/core/kubeArmor.go | 6 +- pkg/KubeArmorOperator/Dockerfile | 30 + pkg/KubeArmorOperator/Dockerfile.ubi | 53 ++ pkg/KubeArmorOperator/Makefile | 151 ++++ pkg/KubeArmorOperator/PROJECT | 20 + .../api/operator.kubearmor.com/v1/common.go | 7 + .../v1/groupversion_info.go | 28 + .../v1/kubearmorconfig_types.go | 82 ++ .../v1/zz_generated.deepcopy.go | 122 +++ pkg/KubeArmorOperator/bundle.Dockerfile | 15 + ...earmor-operator.clusterserviceversion.yaml | 154 ++++ .../bundle/metadata/annotations.yaml | 11 + .../client/clientset/versioned/clientset.go | 84 +++ .../client/clientset/versioned/doc.go | 7 + .../versioned/fake/clientset_generated.go | 72 ++ .../client/clientset/versioned/fake/doc.go | 7 + .../clientset/versioned/fake/register.go | 43 ++ .../client/clientset/versioned/scheme/doc.go | 7 + .../clientset/versioned/scheme/register.go | 43 ++ .../typed/operator.kubearmor.com/v1/doc.go | 7 + .../operator.kubearmor.com/v1/fake/doc.go | 7 + .../v1/fake/fake_kubearmorconfig.go | 129 ++++ .../fake_operator.kubearmor.com_client.go | 27 + .../v1/generated_expansion.go | 8 + .../v1/kubearmorconfig.go | 182 +++++ .../v1/operator.kubearmor.com_client.go | 76 ++ .../informers/externalversions/factory.go | 167 +++++ .../informers/externalversions/generic.go | 49 ++ .../internalinterfaces/factory_interfaces.go | 27 + .../operator.kubearmor.com/interface.go | 33 + .../operator.kubearmor.com/v1/interface.go | 32 + .../v1/kubearmorconfig.go | 77 ++ .../v1/expansion_generated.go | 14 + .../v1/kubearmorconfig.go | 86 +++ pkg/KubeArmorOperator/cmd/main.go | 45 ++ pkg/KubeArmorOperator/cmd/operator/root.go | 78 ++ pkg/KubeArmorOperator/cmd/snitch-cmd/root.go | 146 ++++ pkg/KubeArmorOperator/common/defaults.go | 309 ++++++++ pkg/KubeArmorOperator/common/tls.go | 124 +++ ...erator.kubearmor.com_kubearmorconfigs.yaml | 147 ++++ .../config/crd/kustomization.yaml | 21 + .../config/crd/kustomizeconfig.yaml | 19 + .../cainjection_in_kubearmorconfigs.yaml | 7 + .../patches/webhook_in_kubearmorconfigs.yaml | 16 + .../config/default/kustomization.yaml | 9 + .../config/operator/deployment.yaml | 26 + .../config/operator/kustomization.yaml | 5 + .../config/rbac/clusterrole.yaml | 224 ++++++ .../config/rbac/clusterrolebinding.yaml | 38 + .../config/rbac/kustomization.yaml | 7 + .../config/rbac/service-account.yaml | 4 + .../config/samples/kubearmor-test.yaml | 28 + .../config/samples/kustomization.yaml | 4 + .../samples/operator_v1_kubearmorconfig.yaml | 12 + .../config/samples/sample-config.yml | 28 + .../deployments/helm/.helmignore | 23 + .../deployments/helm/Chart.yaml | 21 + ...erator.kubearmor.com_kubearmorconfigs.yaml | 147 ++++ .../templates/clusterrole-binding-rbac.yaml | 38 + .../helm/templates/clusterrole-rbac.yaml | 224 ++++++ .../helm/templates/deployment.yaml | 31 + .../helm/templates/serviceaccount.yaml | 5 + .../deployments/helm/values.yaml | 9 + .../deployments/operator.yaml | 443 +++++++++++ pkg/KubeArmorOperator/enforcer/enforcer.go | 99 +++ pkg/KubeArmorOperator/go.mod | 76 ++ pkg/KubeArmorOperator/go.sum | 639 ++++++++++++++++ pkg/KubeArmorOperator/hack/boilerplate.go.txt | 2 + .../internal/controller/cluster.go | 509 +++++++++++++ .../internal/controller/resources.go | 708 ++++++++++++++++++ pkg/KubeArmorOperator/k8s/client.go | 119 +++ pkg/KubeArmorOperator/runtime/runtime.go | 46 ++ 77 files changed, 6392 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/ci-operator-release.yaml create mode 100644 .github/workflows/ci-test-operator.yaml create mode 100644 pkg/KubeArmorOperator/Dockerfile create mode 100644 pkg/KubeArmorOperator/Dockerfile.ubi create mode 100644 pkg/KubeArmorOperator/Makefile create mode 100644 pkg/KubeArmorOperator/PROJECT create mode 100644 pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/common.go create mode 100644 pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/groupversion_info.go create mode 100644 pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/kubearmorconfig_types.go create mode 100644 pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/zz_generated.deepcopy.go create mode 100644 pkg/KubeArmorOperator/bundle.Dockerfile create mode 100644 pkg/KubeArmorOperator/bundle/manifests/kubearmor-operator.clusterserviceversion.yaml create mode 100644 pkg/KubeArmorOperator/bundle/metadata/annotations.yaml create mode 100644 pkg/KubeArmorOperator/client/clientset/versioned/clientset.go create mode 100644 pkg/KubeArmorOperator/client/clientset/versioned/doc.go create mode 100644 pkg/KubeArmorOperator/client/clientset/versioned/fake/clientset_generated.go create mode 100644 pkg/KubeArmorOperator/client/clientset/versioned/fake/doc.go create mode 100644 pkg/KubeArmorOperator/client/clientset/versioned/fake/register.go create mode 100644 pkg/KubeArmorOperator/client/clientset/versioned/scheme/doc.go create mode 100644 pkg/KubeArmorOperator/client/clientset/versioned/scheme/register.go create mode 100644 pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/doc.go create mode 100644 pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/fake/doc.go create mode 100644 pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/fake/fake_kubearmorconfig.go create mode 100644 pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/fake/fake_operator.kubearmor.com_client.go create mode 100644 pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/generated_expansion.go create mode 100644 pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/kubearmorconfig.go create mode 100644 pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/operator.kubearmor.com_client.go create mode 100644 pkg/KubeArmorOperator/client/informers/externalversions/factory.go create mode 100644 pkg/KubeArmorOperator/client/informers/externalversions/generic.go create mode 100644 pkg/KubeArmorOperator/client/informers/externalversions/internalinterfaces/factory_interfaces.go create mode 100644 pkg/KubeArmorOperator/client/informers/externalversions/operator.kubearmor.com/interface.go create mode 100644 pkg/KubeArmorOperator/client/informers/externalversions/operator.kubearmor.com/v1/interface.go create mode 100644 pkg/KubeArmorOperator/client/informers/externalversions/operator.kubearmor.com/v1/kubearmorconfig.go create mode 100644 pkg/KubeArmorOperator/client/listers/operator.kubearmor.com/v1/expansion_generated.go create mode 100644 pkg/KubeArmorOperator/client/listers/operator.kubearmor.com/v1/kubearmorconfig.go create mode 100644 pkg/KubeArmorOperator/cmd/main.go create mode 100644 pkg/KubeArmorOperator/cmd/operator/root.go create mode 100644 pkg/KubeArmorOperator/cmd/snitch-cmd/root.go create mode 100644 pkg/KubeArmorOperator/common/defaults.go create mode 100644 pkg/KubeArmorOperator/common/tls.go create mode 100644 pkg/KubeArmorOperator/config/crd/bases/operator.kubearmor.com_kubearmorconfigs.yaml create mode 100644 pkg/KubeArmorOperator/config/crd/kustomization.yaml create mode 100644 pkg/KubeArmorOperator/config/crd/kustomizeconfig.yaml create mode 100644 pkg/KubeArmorOperator/config/crd/patches/cainjection_in_kubearmorconfigs.yaml create mode 100644 pkg/KubeArmorOperator/config/crd/patches/webhook_in_kubearmorconfigs.yaml create mode 100644 pkg/KubeArmorOperator/config/default/kustomization.yaml create mode 100644 pkg/KubeArmorOperator/config/operator/deployment.yaml create mode 100644 pkg/KubeArmorOperator/config/operator/kustomization.yaml create mode 100644 pkg/KubeArmorOperator/config/rbac/clusterrole.yaml create mode 100644 pkg/KubeArmorOperator/config/rbac/clusterrolebinding.yaml create mode 100644 pkg/KubeArmorOperator/config/rbac/kustomization.yaml create mode 100644 pkg/KubeArmorOperator/config/rbac/service-account.yaml create mode 100644 pkg/KubeArmorOperator/config/samples/kubearmor-test.yaml create mode 100644 pkg/KubeArmorOperator/config/samples/kustomization.yaml create mode 100644 pkg/KubeArmorOperator/config/samples/operator_v1_kubearmorconfig.yaml create mode 100644 pkg/KubeArmorOperator/config/samples/sample-config.yml create mode 100644 pkg/KubeArmorOperator/deployments/helm/.helmignore create mode 100644 pkg/KubeArmorOperator/deployments/helm/Chart.yaml create mode 100644 pkg/KubeArmorOperator/deployments/helm/crds/operator.kubearmor.com_kubearmorconfigs.yaml create mode 100644 pkg/KubeArmorOperator/deployments/helm/templates/clusterrole-binding-rbac.yaml create mode 100644 pkg/KubeArmorOperator/deployments/helm/templates/clusterrole-rbac.yaml create mode 100644 pkg/KubeArmorOperator/deployments/helm/templates/deployment.yaml create mode 100644 pkg/KubeArmorOperator/deployments/helm/templates/serviceaccount.yaml create mode 100644 pkg/KubeArmorOperator/deployments/helm/values.yaml create mode 100644 pkg/KubeArmorOperator/deployments/operator.yaml create mode 100644 pkg/KubeArmorOperator/enforcer/enforcer.go create mode 100644 pkg/KubeArmorOperator/go.mod create mode 100644 pkg/KubeArmorOperator/go.sum create mode 100644 pkg/KubeArmorOperator/hack/boilerplate.go.txt create mode 100644 pkg/KubeArmorOperator/internal/controller/cluster.go create mode 100644 pkg/KubeArmorOperator/internal/controller/resources.go create mode 100644 pkg/KubeArmorOperator/k8s/client.go create mode 100644 pkg/KubeArmorOperator/runtime/runtime.go diff --git a/.github/workflows/ci-operator-release.yaml b/.github/workflows/ci-operator-release.yaml new file mode 100644 index 0000000000..84f7267e1d --- /dev/null +++ b/.github/workflows/ci-operator-release.yaml @@ -0,0 +1,43 @@ +name: ci-release-operator + +on: + push: + branches: + - "main" + paths: + - "pkg/KubeArmorOperator/**" + +env: + PLATFORM: linux/amd64,linux/arm64/v8 + +jobs: + kubearmor-operator-release: + name: Build & Push KubeArmor Operator + defaults: + run: + working-directory: ./pkg/KubeArmorOperator + runs-on: ubuntu-20.04 + timeout-minutes: 60 + steps: + - uses: actions/setup-go@v3 + with: + go-version: "v1.20" + + - uses: actions/checkout@v3 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + platforms: linux/amd64,linux/arm64/v8 + + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_AUTHTOK }} + + - name: Build & Push KubeArmor Operator + run: PLATFORM=$PLATFORM make docker-buildx TAG=latest diff --git a/.github/workflows/ci-test-ginkgo.yml b/.github/workflows/ci-test-ginkgo.yml index 160b83d958..263f641256 100644 --- a/.github/workflows/ci-test-ginkgo.yml +++ b/.github/workflows/ci-test-ginkgo.yml @@ -48,28 +48,32 @@ jobs: - name: Generate KubeArmor artifacts run: | GITHUB_SHA=$GITHUB_SHA ./KubeArmor/build/build_kubearmor.sh + + - name: Build Kubearmor-Operator + working-directory: pkg/KubeArmorOperator + run: | + make docker-build - name: Run KubeArmor + working-directory: pkg/KubeArmorOperator run: | if [ ${{ matrix.runtime }} == "containerd" ]; then docker save kubearmor/kubearmor-init:latest | sudo k3s ctr images import - docker save kubearmor/kubearmor:latest | sudo k3s ctr images import - - - helm upgrade --install kubearmor ./deployments/helm \ - --values ./KubeArmor/build/kubearmor-helm-test-values.yaml \ - --set environment.name=k3s \ - -n kube-system; + docker save kubearmor/kubearmor-operator:latest | sudo k3s ctr images import - else if [ ${{ matrix.runtime }} == "crio" ]; then sudo podman pull docker-daemon:kubearmor/kubearmor-init:latest sudo podman pull docker-daemon:kubearmor/kubearmor:latest + sudo podman pull docker-daemon:kubearmor/kubearmor-operator:latest fi - helm upgrade --install kubearmor ./deployments/helm \ - --values ./KubeArmor/build/kubearmor-helm-test-values.yaml \ - --set environment.name=${{ matrix.runtime }} \ - -n kube-system; fi - kubectl wait --for=condition=ready --timeout=5m -n kube-system pod -l kubearmor-app + helm upgrade --install kubearmor-operator ./deployments/helm -n kube-system + kubectl wait --for=condition=ready --timeout=5m -n kube-system pod -l kubearmor-app=kubearmor-operator + kubectl get pods -A + kubectl apply -f ./config/samples/kubearmor-test.yaml + kubectl wait -n kube-system --timeout=5m --for=jsonpath='{.status.phase}'=Running kubearmorconfigs/kubearmorconfig-test + kubectl wait --timeout=5m --for=condition=ready pod -l kubearmor-app,kubearmor-app!=kubearmor-snitch -n kube-system kubectl get pods -A - name: Test KubeArmor using Ginkgo diff --git a/.github/workflows/ci-test-operator.yaml b/.github/workflows/ci-test-operator.yaml new file mode 100644 index 0000000000..457badb9db --- /dev/null +++ b/.github/workflows/ci-test-operator.yaml @@ -0,0 +1,31 @@ +name: ci-test-operator + +on: + push: + branches: + - "main" + paths: + - "pkg/KubeArmorOperator/**" + pull_request: + branches: + - "main" + paths: + - "pkg/KubeArmorOperator/**" + +jobs: + kubearmor-operator-test: + name: Build KubeArmor Operator + defaults: + run: + working-directory: ./pkg/KubeArmorOperator + runs-on: ubuntu-20.04 + timeout-minutes: 20 + steps: + - uses: actions/setup-go@v3 + with: + go-version: v1.20 + + - uses: actions/checkout@v3 + + - name: Build kubearmor operator + run: make docker-build TAG=latest \ No newline at end of file diff --git a/.gitignore b/.gitignore index 028d3b0d2b..927247da75 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ # KubeArmor kubearmor karmor +snitch +kubearmor-operator # KubeArmor YAML Generator deploygen @@ -33,4 +35,4 @@ vmlinux.h **/cflags.lst # gingko test tmp file -**/*.test \ No newline at end of file +**/*.test diff --git a/KubeArmor/common/common.go b/KubeArmor/common/common.go index 696332e469..3271cf7202 100644 --- a/KubeArmor/common/common.go +++ b/KubeArmor/common/common.go @@ -379,14 +379,18 @@ func IsK8sEnv() bool { var ContainerRuntimeSocketMap = map[string][]string{ "docker": { "/var/run/docker.sock", + "/run/docker.sock", }, "containerd": { "/var/snap/microk8s/common/run/containerd.sock", "/run/k3s/containerd/containerd.sock", + "/run/containerd/containerd.sock", "/var/run/containerd/containerd.sock", + "/run/dockershim.sock", }, - "crio": { + "cri-o": { "/var/run/crio/crio.sock", + "/run/crio/crio.sock", }, } diff --git a/KubeArmor/core/kubeArmor.go b/KubeArmor/core/kubeArmor.go index d58e3293bb..b377e5e79a 100644 --- a/KubeArmor/core/kubeArmor.go +++ b/KubeArmor/core/kubeArmor.go @@ -488,7 +488,7 @@ func KubeArmor() { } else if strings.Contains(cfg.GlobalCfg.CRISocket, "containerd") { // monitor containerd events go dm.MonitorContainerdEvents() - } else if strings.Contains(cfg.GlobalCfg.CRISocket, "crio") { + } else if strings.Contains(cfg.GlobalCfg.CRISocket, "cri-o") { // monitor crio events go dm.MonitorCrioEvents() } else { @@ -520,7 +520,7 @@ func KubeArmor() { } else if strings.Contains(dm.Node.ContainerRuntimeVersion, "containerd") { // monitor containerd events go dm.MonitorContainerdEvents() - } else if strings.Contains(dm.Node.ContainerRuntimeVersion, "crio") { + } else if strings.Contains(dm.Node.ContainerRuntimeVersion, "cri-o") { // monitor crio events go dm.MonitorCrioEvents() } else { @@ -582,7 +582,7 @@ func KubeArmor() { return } } else if strings.HasPrefix(dm.Node.ContainerRuntimeVersion, "cri-o") { // cri-o - socketFile := kl.GetCRISocket("crio") + socketFile := kl.GetCRISocket("cri-o") if socketFile != "" { cfg.GlobalCfg.CRISocket = "unix://" + socketFile diff --git a/pkg/KubeArmorOperator/Dockerfile b/pkg/KubeArmorOperator/Dockerfile new file mode 100644 index 0000000000..560e1365c9 --- /dev/null +++ b/pkg/KubeArmorOperator/Dockerfile @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Authors of KubeArmor + +FROM docker.io/golang:1.20 as builder +ARG GOARCH +ARG GOOS +WORKDIR /app +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum +# Copy the go source + +# 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 api api +COPY client client +COPY cmd cmd +COPY common common +COPY internal/controller internal/controller +COPY enforcer enforcer +COPY k8s k8s +COPY runtime runtime +# Build +RUN CGO_ENABLED=0 GOOS=${GOOS} GOARCH=${GOARCH} GO111MODULE=on go build -a -o operator cmd/main.go + +FROM scratch +COPY --from=builder /app/operator /operator +ENTRYPOINT ["/operator"] diff --git a/pkg/KubeArmorOperator/Dockerfile.ubi b/pkg/KubeArmorOperator/Dockerfile.ubi new file mode 100644 index 0000000000..60895308f0 --- /dev/null +++ b/pkg/KubeArmorOperator/Dockerfile.ubi @@ -0,0 +1,53 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2022 Authors of KubeArmor + +FROM redhat/ubi9-minimal as builder +WORKDIR /app +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum +# Copy the go source +RUN microdnf -y update && \ + microdnf -y install --nodocs --setopt=install_weak_deps=0 --setopt=keepcache=0 tar gzip && \ + microdnf clean all && \ + rm -rf /var/cache/yum +# install go +RUN curl -sfL -o go1.19.tar.gz https://go.dev/dl/go1.19.linux-amd64.tar.gz && \ + rm -rf /usr/local/go && tar -C /usr/local -xzf go1.19.tar.gz && \ + rm go1.19.tar.gz +ENV PATH=${PATH}:/usr/local/go/bin +# 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 cmd cmd +COPY common common +COPY internal/controller internal/controller +COPY enforcer enforcer +COPY k8s k8s +COPY runtime runtime +# Build +RUN CGO_ENABLED=0 GO111MODULE=on go build -a -o kubearmor-operator cmd/main.go +RUN ln -s kubearmor-operator snitch + +FROM redhat/ubi9-minimal +LABEL name="kubearmor-operator" \ + vendor="Accuknox" \ + version="1.0.0" \ + release="1.0.0" \ + summary="kubearmor-operator container image based on redhat ubi" \ + description="kubearmor-operator to deploy and manage KubeArmor" + +RUN microdnf -y update && \ + microdnf -y install --nodocs --setopt=install_weak_deps=0 --setopt=keepcache=0 shadow-utils && \ + microdnf clean all && \ + rm -rf /var/cache/yum + +RUN groupadd --gid 1000 default \ + && useradd --uid 1000 --gid default --shell /bin/bash --create-home default + +COPY --from=builder /app/kubearmor-operator /kubearmor-operator +RUN ln -s /kubearmor-operator /snitch + +USER default +ENTRYPOINT ["/kubearmor-operator"] diff --git a/pkg/KubeArmorOperator/Makefile b/pkg/KubeArmorOperator/Makefile new file mode 100644 index 0000000000..9022869173 --- /dev/null +++ b/pkg/KubeArmorOperator/Makefile @@ -0,0 +1,151 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright 2021 Authors of KubeArmor + +CURDIR := $(shell pwd) + +# Image URL to use all building/pushing image targets +IMG ?= kubearmor/kubearmor-operator +# Image Tag to use all building/pushing image targets +TAG ?= v0.1 +# Produce CRDs that work back to Kubernetes 1.11 (no version conversion) +CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false" +# Target platforms for build +PLATFORM ?= "linux/amd64,linux/arm64/v8" + +BUNDLE_PKG ?= kubearmor-operator +BUNDLE_VER ?= latest + +# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) +ifeq (,$(shell go env GOBIN)) +GOBIN=$(shell go env GOPATH)/bin +else +GOBIN=$(shell go env GOBIN) +endif + +# Setting SHELL to bash allows bash commands to be executed by recipes. +# This is a requirement for 'setup-envtest.sh' in the test target. +# Options are set to exit when a recipe line exits non-zero or a piped command fails. +SHELL = /usr/bin/env bash -o pipefail +.SHELLFLAGS = -ec + +.PHONY: all +all: get snitch kubearmor-operator + +##@ General + +# The help target prints out all targets with their descriptions organized +# beneath their categories. The categories are represented by '##@' and the +# target descriptions by '##'. The awk commands is responsible for reading the +# entire set of makefiles included in this invocation, looking for lines of the +# file as xyz: ## something, and then pretty-format the target and help. Then, +# if there's a line with ##@ something, that gets pretty-printed as a category. +# More info on the usage of ANSI control characters for terminal formatting: +# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters +# More info on the awk command: +# http://linuxcommand.org/lc3_adv_awk.php + +.PHONY: fmt +fmt: ## Run go fmt against code. + go fmt ./... + +.PHONY: vet +vet: ## Run go vet against code. + go vet ./... + +.PHONY: get +get: tidy + go get ./... + +.PHONY: tidy +tidy: + go mod tidy + +.PHONY: kubearmor-operator +kubearmor-operator: get + go build -o kubearmor-operator cmd/main.go + +.PHONY: snitch +snitch: get + go build -o snitch cmd/main.go + +.PHONY: build +build: snitch kubearmor-operator + +.PHONY: docker-build +docker-build: ## Build docker image with the manager. + docker build -t ${IMG}:${TAG} -t ${IMG}:latest . + +.PHONY: docker-push +docker-push: ## Push docker image with the manager. + docker push ${IMG}:${TAG} + docker push ${IMG}:latest + +docker-buildx: + docker buildx build --platform ${PLATFORM} --push -t ${IMG}:${TAG} . + +KUSTOMIZE = /usr/local/bin/kustomize +.PHONY: kustomize +kustomize: ## Download kustomize locally if necessary. + $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7) + +OPERATOR_SDK = /usr/local/bin/operator-sdk +.PHONY: operator-sdk +operator-sdk: + @echo "Installing Operator SDK..." + curl -LO https://github.com/operator-framework/operator-sdk/releases/latest/download/operator-sdk_linux_amd64 + chmod +x operator-sdk_linux_amd64 + sudo mv operator-sdk_linux_amd64 /usr/local/bin/operator-sdk + +ifndef ignore-not-found + ignore-not-found = false +endif + +.PHONY: install +install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. + $(KUSTOMIZE) build config/crd | kubectl apply -f - + +.PHONY: uninstall +uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f - + +.PHONY: deploy +deploy: kustomize ## Deploy Operator Deployment, ClusterRole and ServiceAccount objects. + $(KUSTOMIZE) build config/default | kubectl apply -f - + +.PHONY: undeploy +undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. + $(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f - + +.PHONY: manifests +manifests: controller-gen kustomize## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. + go mod tidy; $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=config/crd/bases + rm -r deployments/helm/crds/* && cp config/crd/bases/* deployments/helm/crds/ + $(KUSTOMIZE) build config/default | tee deployments/operator.yaml + +.PHONY: generate +generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. + go mod tidy; $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + +CONTROLLER_GEN = $(GOBIN)/controller-gen +.PHONY: controller-gen +controller-gen: ## Download controller-gen locally if necessary. + $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1) + +client/gen: + @echo "--> Running code-generator to generate clients" + # prepare tool code-generator + @mkdir -p ./tmp/code-generator + @git clone https://github.com/kubernetes/code-generator.git --branch v0.22.1 --single-branch ./tmp/code-generator + # generate client + GOPATH= GOROOT= ./tmp/code-generator/generate-groups.sh "all" github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/api operator.kubearmor.com:v1 --go-header-file hack/boilerplate.go.txt + # check generated client at ./pkg/client + @cp -r ./github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/* ./client/ + @rm -rf ./github.com ./tmp/code-generator + +.PHONY: bundle +bundle: operator-sdk + ${OPERATOR_SDK} generate bundle --version ${BUNDLE_VER} --input-dir ./config --output-dir ./bundle --package ${BUNDLE_PKG} + +.PHONY: clean +clean: + rm snitch kubearmor-operator \ No newline at end of file diff --git a/pkg/KubeArmorOperator/PROJECT b/pkg/KubeArmorOperator/PROJECT new file mode 100644 index 0000000000..61b62418ab --- /dev/null +++ b/pkg/KubeArmorOperator/PROJECT @@ -0,0 +1,20 @@ +# Code generated by tool. DO NOT EDIT. +# This file is used to track the info used to scaffold your project +# and allow the plugins properly work. +# More info: https://book.kubebuilder.io/reference/project-config.html +domain: kubearmor.com +layout: +- go.kubebuilder.io/v4 +projectName: kubearmoroperator +repo: github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator +resources: +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: kubearmor.com + group: operator + kind: KubeArmorConfig + path: github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/api/v1 + version: v1 +version: "3" diff --git a/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/common.go b/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/common.go new file mode 100644 index 0000000000..5c466a764a --- /dev/null +++ b/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/common.go @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Authors of KubeArmor + +package v1 + +// +kubebuilder:validation:Enum=audit;block +type PostureType string diff --git a/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/groupversion_info.go b/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/groupversion_info.go new file mode 100644 index 0000000000..21f3c1a400 --- /dev/null +++ b/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/groupversion_info.go @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Package v1 contains API Schema definitions for the operator v1 API group +// +kubebuilder:object:generate=true +// +groupName=operator.kubearmor.com +package v1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: "operator.kubearmor.com", Version: "v1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/kubearmorconfig_types.go b/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/kubearmorconfig_types.go new file mode 100644 index 0000000000..dea3f0d50b --- /dev/null +++ b/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/kubearmorconfig_types.go @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// ImageSpec defines the image specifications +type ImageSpec struct { + // +kubebuilder:validation:optional + Image string `json:"image,omitempty"` + // +kubebuilder:validation:optional + // +kubebuilder:validation:Enum=Always;IfNotPresent;Never + // +kubebuilder:default:=Always + ImagePullPolicy string `json:"imagePullPolicy,omitempty"` +} + +// KubeArmorConfigSpec defines the desired state of KubeArmorConfig +type KubeArmorConfigSpec struct { + // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + // Important: Run "make" to regenerate code after modifying this file + + // +kubebuilder:validation:optional + DefaultFilePosture PostureType `json:"defaultFilePosture,omitempty"` + // +kubebuilder:validation:optional + DefaultCapabilitiesPosture PostureType `json:"defaultCapabilitiesPosture,omitempty"` + // +kubebuilder:validation:optional + DefaultNetworkPosture PostureType `json:"defaultNetworkPosture,omitempty"` + // +kubebuilder:validation:optional + DefaultVisibility string `json:"defaultVisibility,omitempty"` + // +kubebuilder:validation:optional + KubeArmorImage ImageSpec `json:"kubearmorImage,omitempty"` + // +kubebuilder:validation:optional + KubeArmorInitImage ImageSpec `json:"kubearmorInitImage,omitempty"` + // +kubebuilder:validation:optional + KubeArmorRelayImage ImageSpec `json:"kubearmorRelayImage,omitempty"` + // +kubebuilder:validation:optional + KubeArmorControllerImage ImageSpec `json:"kubearmorControllerImage,omitempty"` + // +kubebuilder:validation:optional + KubeRbacProxyImage ImageSpec `json:"kubeRbacProxyImage,omitempty"` +} + +// KubeArmorConfigStatus defines the observed state of KubeArmorConfig +type KubeArmorConfigStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file + // +kubebuilder:validation:optional + Phase string `json:"phase,omitempty"` + // +kubebuilder:validation:optional + Message string `json:"message,omitempty"` +} + +// +genclient +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Status",type="string",JSONPath=".status.phase" +// KubeArmorConfig is the Schema for the KubeArmorConfigs API +type KubeArmorConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec KubeArmorConfigSpec `json:"spec,omitempty"` + Status KubeArmorConfigStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// KubeArmorConfigList contains a list of KubeArmorConfig +type KubeArmorConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []KubeArmorConfig `json:"items"` +} + +func init() { + SchemeBuilder.Register(&KubeArmorConfig{}, &KubeArmorConfigList{}) +} diff --git a/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/zz_generated.deepcopy.go b/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/zz_generated.deepcopy.go new file mode 100644 index 0000000000..8bdfa45997 --- /dev/null +++ b/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1/zz_generated.deepcopy.go @@ -0,0 +1,122 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by controller-gen. DO NOT EDIT. + +package v1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageSpec) DeepCopyInto(out *ImageSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageSpec. +func (in *ImageSpec) DeepCopy() *ImageSpec { + if in == nil { + return nil + } + out := new(ImageSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeArmorConfig) DeepCopyInto(out *KubeArmorConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeArmorConfig. +func (in *KubeArmorConfig) DeepCopy() *KubeArmorConfig { + if in == nil { + return nil + } + out := new(KubeArmorConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KubeArmorConfig) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeArmorConfigList) DeepCopyInto(out *KubeArmorConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KubeArmorConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeArmorConfigList. +func (in *KubeArmorConfigList) DeepCopy() *KubeArmorConfigList { + if in == nil { + return nil + } + out := new(KubeArmorConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KubeArmorConfigList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeArmorConfigSpec) DeepCopyInto(out *KubeArmorConfigSpec) { + *out = *in + out.KubeArmorImage = in.KubeArmorImage + out.KubeArmorInitImage = in.KubeArmorInitImage + out.KubeArmorRelayImage = in.KubeArmorRelayImage + out.KubeArmorControllerImage = in.KubeArmorControllerImage + out.KubeRbacProxyImage = in.KubeRbacProxyImage +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeArmorConfigSpec. +func (in *KubeArmorConfigSpec) DeepCopy() *KubeArmorConfigSpec { + if in == nil { + return nil + } + out := new(KubeArmorConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubeArmorConfigStatus) DeepCopyInto(out *KubeArmorConfigStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubeArmorConfigStatus. +func (in *KubeArmorConfigStatus) DeepCopy() *KubeArmorConfigStatus { + if in == nil { + return nil + } + out := new(KubeArmorConfigStatus) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/KubeArmorOperator/bundle.Dockerfile b/pkg/KubeArmorOperator/bundle.Dockerfile new file mode 100644 index 0000000000..b74c37194d --- /dev/null +++ b/pkg/KubeArmorOperator/bundle.Dockerfile @@ -0,0 +1,15 @@ +FROM scratch + +# Core bundle labels. +LABEL operators.operatorframework.io.bundle.mediatype.v1=registry+v1 +LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/ +LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/ +LABEL operators.operatorframework.io.bundle.package.v1=kubearmor-operator +LABEL operators.operatorframework.io.bundle.channels.v1=alpha +LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.28.0+git +LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1 +LABEL operators.operatorframework.io.metrics.project_layout=unknown + +# Copy files to locations specified by labels. +COPY ./bundle/manifests /manifests/ +COPY ./bundle/metadata /metadata/ diff --git a/pkg/KubeArmorOperator/bundle/manifests/kubearmor-operator.clusterserviceversion.yaml b/pkg/KubeArmorOperator/bundle/manifests/kubearmor-operator.clusterserviceversion.yaml new file mode 100644 index 0000000000..7191960a37 --- /dev/null +++ b/pkg/KubeArmorOperator/bundle/manifests/kubearmor-operator.clusterserviceversion.yaml @@ -0,0 +1,154 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + annotations: + alm-examples: '[]' + capabilities: Basic Install + createdAt: "2023-05-01T05:17:07Z" + categories: Security + description: The KubeArmor Operator deloys and manages KubeArmor daemonset and its componenets within a cluster. + support: Accuknox + repository: https://github.com/kubearmor/KubeArmor + operators.operatorframework.io/builder: operator-sdk-v1.28.0+git + operators.operatorframework.io/project_layout: unknown + operators.openshift.io/valid-subscription: "" + com.redhat.openshift.versions: v4.12 + name: kubearmor-operator.v1.0.0 + namespace: placeholder +spec: + relatedImages: + - image: docker.io/kubearmor/kubearmor@sha256:52f7049ecdbfa779663d2cd44ddd7f36aecd8ccd2ca91e69709b628600029f13 + name: kubearmor + - image: docker.io/kubearmor/kubearmor-init@sha256:44fa534587972c5f85e3e6f362dd8268e9088a33c5657a21b72a7e8563164fb5 + name: kubearmor-init + - image: docker.io/kubearmor/kubearmor-relay-server@sha256:695f9ade3382bcf858077e2a7b9b01e0713071517603778d72f7aca5a6d71323 + name: kubearmor-relay-server + - image: docker.io/kubearmor/kubearmor-controller@sha256:ccec0fb8bb1e91bd48631cc89baf752b842f19c33b50edab08f8b33e57af64d4 + name: kubearmor-controller + apiservicedefinitions: {} + customresourcedefinitions: {} + description: |- + The KubeArmor Operator deploys and manages KubeArmor and its components: + - KubeArmor Daemonset + - KubeArmor Relay Server + - KubeArmor Controller to manage CRDs (KubeArmorPolicy, KubeArmorHostPolicy) + + KubeArmor is a cloud-native runtime security enforcement system that restricts the behavior (such as process execution, file access, and networking operations) of pods, containers, and nodes (VMs) at the system level. + + KubeArmor leverages Linux security modules (LSMs) such as AppArmor, SELinux, or BPF-LSM to enforce the user-specified policies. KubeArmor generates rich alerts/telemetry events with container/pod/namespace identities by leveraging eBPF. + + ## Documentation + + github: [https://github.com/kubearmor/KubeArmor](https://github.com/kubearmor/KubeArmor) + website: [https://kubearmor.io/](https://kubearmor.io/) + displayName: Kubearmor Operator + icon: + - base64data: "AAABAAEAIB4AAAEAIACgDwAAFgAAACgAAAAgAAAAPAAAAAEAIAAAAAAAAA8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPHMAAD/2gAA+9QAD/3WADf/2gBJ/9ABUv/GA1//xQRg/8UEYP/FBGD/xQRg/8UEYP/FBGD/xQRg/8UEYP/FBF/+wAVL+qcJHqZiDAD5kg0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/NUAAPrTAA3+1gA9/9gATP+3Bnb/ngvF/5oM4v+aDOP/mgzj/5oM4/+aDOP/mgzj/5oM4/+aDOP/mgzj/5oM4/+aDOP+lw3S/JUNffeRDQ/3kQ0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPrSAAD61AAI/tYAOf/ZAEv/swd+/5kM6/+WDf//lw3//5gN//+YDf//mA3//5gN//+YDf//mA3//5gN//+YDf//mA3//5gN//+XDf//lg39/ZQNivSQDQbzkA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD40QAA99EABP3WADH/2gBK/7oGcP+ZDOT/lg3//JQN/9p6Cf/Kbwj/ym8I/8pvCP/Kbwj/ym8I/8pvCP/Kbwj/ym8I/8pvCP/Lbwj/64cL//+XDf//lg31/JQNZd+FDAHzkA0AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA9M4AAPbPAAH81QAn/9kASv/EBGH/nAzV/5YN//yVDf+8ZAf/ezQB/3UwAP91MAD/dTAA/3QvAP90LwD/dC8A/3UwAP91MAD/dTAA/3QvAP+YSgP/8YwM//+XDf/+lQ3m+5MNQv+XDQDvjgwAAAAAAAAAAAAAAAAAAAAAAO/KAADNrgAA/NQAHf/YAEj/zQJW/58Lwf+WDf//lw3/yW4I/3o0Af90LwD/dTAA/3UwAP91MAD/g0Ua/4FCFv+BQRX/dC8A/3UwAP91MAD/dTAA/3QvAP+rWAX/+ZIM//+WDf/+lQ3O+ZMNJvqUDQAAAAAAAAAAAAAAAADpxgAA/9gAAPvUABP+1wBE/9UBT/+lCqn/lg39/5cN/9l6Cf+BOQH/dC8A/3UwAP91MAD/dC8A/4BBFP/GqZX/oXJR/8Kkjv94NQf/dTAA/3UwAP91MAD/dTAA/3cyAP+/Zwf//pYN//+WDf/9lQ2v95INEvaRDQAAAAAAAAAAAPrTAAD60wAL/tYAPf/ZAEz/rAmP/5cN9/+XDf/nhQv/jEEC/3MvAP91MAD/dTAA/3UwAP9zLQD/r4dr/8uxn/9/PxT/2ca4/6R2Vv9zLQD/dTAA/3UwAP91MAD/dC8A/342Af/Tdgn//5cN//+WDfz8lQ2K848NBvKPDQAAAAAA+NIABv3WADP/2gBL/7YGeP+YDez/lw3/8o0M/5tMA/9zLwD/dTAA/3UwAP91MAD/cy0A/5djP//r4dr/qoBi/3EqAP+5ln7/6NzU/5lnQ/90LwD/dC8A/3UwAP91MAD/dC8A/4k/Av/kggr//5cN//+WDfX8lA1k2IAMAeWIDAD91QAt/9kASv/BBGT/mwzb/5YN//qTDf+tWgX/dC8A/3UwAP91MAD/dTAA/3czBf+jdVX/y7Ge/9jEtv+LUSn/ciwA/5JcNv/Tva7/xqqW/7CIbf99Ow//dS8A/3UwAP91MAD/cy8A/5hKA//xjAz//5cN//6WDeX7kw1B/5wNAP7XAEn/zgJV/58Lwv+WDf/+lg3/wmkH/3gyAP91MAD/dTAA/3UwAP90LwD/gUIW/8yzof+dbEn/wqSP/3k3CP91LwD/ezkL/8Smkf+RWjP/yKyZ/5djPv9zLQD/dTAA/3UwAP91MAD/dC8A/6tYBf/5kgz//5YN//6VDcz6kw0m/toAR/+3BnX/lw33/5cN/9Z4Cf9/NwH/dC8A/3UwAP91MAD/dTAA/3UvAP99PA//w6SP/7GJbv+thGj/cy0A/3UwAP9zLQD/rINm/6l/Yf+8moP/kFgy/3MuAP91MAD/dTAA/3UwAP91MAD/dzIA/8FoB//+lg3//5YN//qTDXn92gA7/7YHeP+XDfj/lw3/tF4G/3MuAP91MAD/dTAA/3UwAP91MAD/dS8A/39AE//Hq5f/ya6b/5BYMf9zLgD/dTAA/3QuAP+LUCj/xKaR/8Cgiv+UXjn/cy0A/3UwAP91MAD/dTAA/3UwAP9zLgD/mUsD//uTDf//lg3/+pMNfv3YACz/wgRj/5kM6f+YDf/McAj/djEA/3UwAP91MAD/dTAA/3UwAP9zLgD/j1Yv/9C5qf/Gqpb/ezkL/3UvAP91MAD/dTAA/3czBf+/n4j/w6WQ/6qAY/9zLQD/dTAA/3UwAP91MAD/dTAA/3MuAP+uWwX//5YN//+WDfb7lA1S/NUAHP/RAVD/nQzP/5cN/+SCCv9+NwH/dDAA/3UwAP91MAD/dTAA/3QuAP+thGf/z7al/7WQdv92MQT/dzME/3czBP93MwT/dTAE/6h8Xv/Bo43/w6WQ/3w6Df91LwD/dTAA/3UwAP91MAD/dTAA/8htCP//mA3//pYN4vmTDSn60wAP/tkAQ/+kCq3/lg3/9Y8M/49DAv9zLwD/dTAA/3UwAP91MAD/dzQF/8Gjjf+wiW3/z7em/8Kjj//CpI//wqSO/8Kkj//Co4//zrWk/6+Ha/+/oIn/j1cx/3MuAP91MAD/dTAA/3UwAP98NQH/334K//+XDf/+lQ3B9JAMDfbPAAX+2wA4/64Ii/+WDf3+lQ3/plUF/3MuAP91MAD/dTAA/3UwAP92MgL/wKCK/49YMf+HSyP/i1Eo/4pQJ/+NVCz/ilAn/4tRKP+KTyb/fTwP/8Cgiv+MUir/dC4A/3UwAP91MAD/dC8A/4k/Av/wiwz//5cN//2VDZfhhgsB7ccAAP3ZACz/vAVt/5gN8f+YDf/CaQf/dC8A/3UwAP91MAD/dTAA/3QuAP+3k3r/mWZD/3IrAP9zLQD/h0si/8Smkf+PVzD/cy0A/3MuAP99PA7/xqmV/4FCFv90LwD/dTAA/3UwAP9zLgD/nU0E//yTDf//lg3+/JQNaOiKDADwyQAA/NcAHv/LAlj/mwza/5cN/9t8Cv96NAD/dTAA/3UwAP91MAD/cy0A/6N1Vf+xinD/cywA/3UxAv+8moL/u5mB/8Wok/96OAr/ciwA/5JcNv/AoIr/djIE/3UwAP91MAD/dTAA/3MuAP+1Xwb//5cN//+WDfT7kw0+AAAAAAAAAAD71AAR/9YASf+gC7v/lg3/74oL/4c+Av90LwD/dTAA/3UwAP90LwD/g0Ua/8mum/+MUir/eDUG/8Smkv+JTiX/xKaR/4BBFf9/PxX/w6aR/5pnRP9zLgD/dTAA/3UwAP91MAD/djEA/85yCP//mA3//pUN3PmSDR0AAAAAAAAAAPjRAAf+2gA9/6kJmf+WDf/7kw3/nE0E/3MuAP91MAD/dTAA/3UwAP90LgD/mGRB/8mum/+wiGz/x6qX/34+Ef/EppL/t5R7/8eql/+ofF7/djIE/3UwAP91MAD/dTAA/3QwAP9+NwH/5IIK//+XDf/9lQ258o8MCAAAAAAAAAAA8cwAAf3aADL/tgZ5/5cN9/+XDf+2YAb/ci4A/3UwAP91MAD/dTAA/3UwAP90LgD/hUgf/62EZ//EppL/djID/8Cgiv+7mYH/iE0m/3UvAP91MAD/dTAA/3UwAP91MAD/cy8A/41BAv/0jgz//5YN//2VDYzOewkAAAAAAAAAAAD/2AAA/NgAHv/FBF7/mgzm/5cN/9t8Cv+GPQL/dC8A/3QvAP91MAD/dTAA/3UwAP9yLAD/k1w2/7qYgP9yLAD/s41y/5tpRv9yKwD/dTAA/3UwAP91MAD/dTAA/3MvAP92MAD/r1sF//2VDf//lg39/JQNXeaICwAAAAAAAAAAAOjDAAD31QAF/dEBMf+fC7r/lg3//pYN/+iGC/+2YAb/hj0C/3QvAP90LwD/dTAA/3MtAP+gcVD/sotw/3IsA/+rgGP/qHxe/3MsAP91MAD/dTAA/3MvAP94MgD/lkkD/8xwCP/1jwz//5YN//6WDen6kw0rAAAAAAAAAAAAAAAAAAAAAPzYAAD15QAC/aIKNv6WDcf/lg3+/5cN//+WDf/phgv/t2EG/4c9Av90LwD/ciwA/6Z5Wv/Ruqr/vJuH/8+2pv+sgmX/cy0A/3MvAP95MwD/mUsD/89zCP/2kAz//5cN//+WDf/+lg3z/JUNiPOPDQUAAAAAAAAAAAAAAAAAAAAAxK8AAPjeAAD/sQgA+JINGvyUDW/+lQ3P/5YN/f+XDf//lg3/6ocL/7liBv+IPQL/fDwP/4dMJP+JTyX/iE0k/3w8EP96MwD/nE0E/9J1Cf/3kQz//5gN//+WDf//lg3x/ZUNrfqTDUr0jwwJ9pENAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPCODAD5kw0A/50OAPiSDRr9lQ1s/pUNzf+WDfz/lw3//5cN/+uIC/+6YwX/iD0A/3QuAP96MwD/n04D/9V3Cf/5kgz//5gN//+WDf/+lg3v/ZUNp/uUDUXzjw0I/5YNAPWQDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADoiQwA+ZINAP+aDQD5kg0Y/JUNav6WDcv/lg38/5cN//+XDf/siAv/yW4I/9l6Cf/6kwz//5cN//+WDf//lg3s/ZUNofqTDT/vjQwH/pUNAPaRDQCkYAkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA7owMAPmSDQD/mA0A+ZMNF/yUDWb+lg3H/5YN+f+XDf//mA3//5cN//+WDf/+lg3n/ZUNmvuUDTrxjQ0F/ZUNAPSQDADOegkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAPGPDQD6kw0A/5kNAPmTDRj8lA1y/pUN3P+WDf7+lg31/ZUNqfqTDTzoiAsE/ZUNAPaQDQDjhgwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD+AAH//AAAf/gAAD/wAAAf4AAAH+AAAA/AAAAHgAAAAwAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAGAAAABgAAAAYAAAAGAAAADwAAAA8AAAAPgAAAD+AAAB/4AAB//gAB//+AB///4B/8=" + mediatype: "image/x-icon" + install: + spec: + clusterPermissions: + - rules: + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - watch + - list + - patch + - apiGroups: + - "" + resources: + - secrets + - serviceaccounts + - services + verbs: + - get + - create + - delete + - update + - apiGroups: + - apps + resources: + - deployments + - daemonsets + verbs: + - get + - create + - delete + - update + - apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + verbs: + - get + - create + - delete + - apiGroups: + - batch + resources: + - jobs + verbs: + - create + - apiGroups: + - rbac.authorization.k8s.io + resources: + - clusterroles + - clusterrolebindings + verbs: + - create + - get + - apiGroups: + - '*' + resources: + - '*' + verbs: + - '*' + serviceAccountName: kubearmor-operator + deployments: + - name: kubearmor-operator + spec: + selector: + matchLabels: + kubearmor-app: kubearmor-operator + strategy: {} + template: + metadata: + labels: + kubearmor-app: kubearmor-operator + spec: + containers: + - image: docker.io/kubearmor/kubearmor-operator@sha256:8c2d56781a2adf78ee58f956e01417f38b6ad2f319feada6470e93f4f9bf0808 + imagePullPolicy: Always + name: operator + resources: {} + serviceAccountName: kubearmor-operator + strategy: deployment + installModes: + - supported: false + type: OwnNamespace + - supported: false + type: SingleNamespace + - supported: false + type: MultiNamespace + - supported: true + type: AllNamespaces + keywords: + - kubearmor-operator + - runtime-security + - security + - cloud-native + links: + - name: Kubearmor + url: https://kubearmor.io + maintainers: + - email: ramakant@accuknox.com + name: Ramakant Sharma + maturity: alpha + provider: + name: Accuknox + url: https://www.accuknox.com/ + version: 1.0.0 diff --git a/pkg/KubeArmorOperator/bundle/metadata/annotations.yaml b/pkg/KubeArmorOperator/bundle/metadata/annotations.yaml new file mode 100644 index 0000000000..e23d761705 --- /dev/null +++ b/pkg/KubeArmorOperator/bundle/metadata/annotations.yaml @@ -0,0 +1,11 @@ +annotations: + # Core bundle annotations. + operators.operatorframework.io.bundle.mediatype.v1: registry+v1 + operators.operatorframework.io.bundle.manifests.v1: manifests/ + operators.operatorframework.io.bundle.metadata.v1: metadata/ + operators.operatorframework.io.bundle.package.v1: kubearmor-operator + operators.operatorframework.io.bundle.channels.v1: alpha + operators.operatorframework.io.metrics.builder: operator-sdk-v1.28.0+git + operators.operatorframework.io.metrics.mediatype.v1: metrics+v1 + operators.operatorframework.io.metrics.project_layout: unknown + com.redhat.openshift.versions: v4.12 \ No newline at end of file diff --git a/pkg/KubeArmorOperator/client/clientset/versioned/clientset.go b/pkg/KubeArmorOperator/client/clientset/versioned/clientset.go new file mode 100644 index 0000000000..df3851f0c9 --- /dev/null +++ b/pkg/KubeArmorOperator/client/clientset/versioned/clientset.go @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + "fmt" + + operatorv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1" + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + OperatorV1() operatorv1.OperatorV1Interface +} + +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *discovery.DiscoveryClient + operatorV1 *operatorv1.OperatorV1Client +} + +// OperatorV1 retrieves the OperatorV1Client +func (c *Clientset) OperatorV1() operatorv1.OperatorV1Interface { + return c.operatorV1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +// If config's RateLimiter is not set and QPS and Burst are acceptable, +// NewForConfig will generate a rate-limiter in configShallowCopy. +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + if configShallowCopy.Burst <= 0 { + return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") + } + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error + cs.operatorV1, err = operatorv1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + var cs Clientset + cs.operatorV1 = operatorv1.NewForConfigOrDie(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) + return &cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.operatorV1 = operatorv1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/pkg/KubeArmorOperator/client/clientset/versioned/doc.go b/pkg/KubeArmorOperator/client/clientset/versioned/doc.go new file mode 100644 index 0000000000..b72af8f9c9 --- /dev/null +++ b/pkg/KubeArmorOperator/client/clientset/versioned/doc.go @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated clientset. +package versioned diff --git a/pkg/KubeArmorOperator/client/clientset/versioned/fake/clientset_generated.go b/pkg/KubeArmorOperator/client/clientset/versioned/fake/clientset_generated.go new file mode 100644 index 0000000000..c5a3901eda --- /dev/null +++ b/pkg/KubeArmorOperator/client/clientset/versioned/fake/clientset_generated.go @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + clientset "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/clientset/versioned" + operatorv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1" + fakeoperatorv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/fake" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/discovery" + fakediscovery "k8s.io/client-go/discovery/fake" + "k8s.io/client-go/testing" +) + +// NewSimpleClientset returns a clientset that will respond with the provided objects. +// It's backed by a very simple object tracker that processes creates, updates and deletions as-is, +// without applying any validations and/or defaults. It shouldn't be considered a replacement +// for a real clientset and is mostly useful in simple unit tests. +func NewSimpleClientset(objects ...runtime.Object) *Clientset { + o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) + for _, obj := range objects { + if err := o.Add(obj); err != nil { + panic(err) + } + } + + cs := &Clientset{tracker: o} + cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} + cs.AddReactor("*", "*", testing.ObjectReaction(o)) + cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { + gvr := action.GetResource() + ns := action.GetNamespace() + watch, err := o.Watch(gvr, ns) + if err != nil { + return false, nil, err + } + return true, watch, nil + }) + + return cs +} + +// Clientset implements clientset.Interface. Meant to be embedded into a +// struct to get a default implementation. This makes faking out just the method +// you want to test easier. +type Clientset struct { + testing.Fake + discovery *fakediscovery.FakeDiscovery + tracker testing.ObjectTracker +} + +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + return c.discovery +} + +func (c *Clientset) Tracker() testing.ObjectTracker { + return c.tracker +} + +var ( + _ clientset.Interface = &Clientset{} + _ testing.FakeClient = &Clientset{} +) + +// OperatorV1 retrieves the OperatorV1Client +func (c *Clientset) OperatorV1() operatorv1.OperatorV1Interface { + return &fakeoperatorv1.FakeOperatorV1{Fake: &c.Fake} +} diff --git a/pkg/KubeArmorOperator/client/clientset/versioned/fake/doc.go b/pkg/KubeArmorOperator/client/clientset/versioned/fake/doc.go new file mode 100644 index 0000000000..3c40f89bb5 --- /dev/null +++ b/pkg/KubeArmorOperator/client/clientset/versioned/fake/doc.go @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated fake clientset. +package fake diff --git a/pkg/KubeArmorOperator/client/clientset/versioned/fake/register.go b/pkg/KubeArmorOperator/client/clientset/versioned/fake/register.go new file mode 100644 index 0000000000..2f87891d6e --- /dev/null +++ b/pkg/KubeArmorOperator/client/clientset/versioned/fake/register.go @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + operatorv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var scheme = runtime.NewScheme() +var codecs = serializer.NewCodecFactory(scheme) + +var localSchemeBuilder = runtime.SchemeBuilder{ + operatorv1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(scheme)) +} diff --git a/pkg/KubeArmorOperator/client/clientset/versioned/scheme/doc.go b/pkg/KubeArmorOperator/client/clientset/versioned/scheme/doc.go new file mode 100644 index 0000000000..2db6a4f105 --- /dev/null +++ b/pkg/KubeArmorOperator/client/clientset/versioned/scheme/doc.go @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/pkg/KubeArmorOperator/client/clientset/versioned/scheme/register.go b/pkg/KubeArmorOperator/client/clientset/versioned/scheme/register.go new file mode 100644 index 0000000000..0b99f3ace4 --- /dev/null +++ b/pkg/KubeArmorOperator/client/clientset/versioned/scheme/register.go @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + operatorv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) +var localSchemeBuilder = runtime.SchemeBuilder{ + operatorv1.AddToScheme, +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +var AddToScheme = localSchemeBuilder.AddToScheme + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + utilruntime.Must(AddToScheme(Scheme)) +} diff --git a/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/doc.go b/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/doc.go new file mode 100644 index 0000000000..f8ad31ca2f --- /dev/null +++ b/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/doc.go @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1 diff --git a/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/fake/doc.go b/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/fake/doc.go new file mode 100644 index 0000000000..2fb28af17f --- /dev/null +++ b/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/fake/doc.go @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by client-gen. DO NOT EDIT. + +// Package fake has the automatically generated clients. +package fake diff --git a/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/fake/fake_kubearmorconfig.go b/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/fake/fake_kubearmorconfig.go new file mode 100644 index 0000000000..4a87bfc663 --- /dev/null +++ b/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/fake/fake_kubearmorconfig.go @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + "context" + + operatorkubearmorcomv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeKubeArmorConfigs implements KubeArmorConfigInterface +type FakeKubeArmorConfigs struct { + Fake *FakeOperatorV1 + ns string +} + +var kubearmorconfigsResource = schema.GroupVersionResource{Group: "operator.kubearmor.com", Version: "v1", Resource: "kubearmorconfigs"} + +var kubearmorconfigsKind = schema.GroupVersionKind{Group: "operator.kubearmor.com", Version: "v1", Kind: "KubeArmorConfig"} + +// Get takes name of the kubeArmorConfig, and returns the corresponding kubeArmorConfig object, and an error if there is any. +func (c *FakeKubeArmorConfigs) Get(ctx context.Context, name string, options v1.GetOptions) (result *operatorkubearmorcomv1.KubeArmorConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewGetAction(kubearmorconfigsResource, c.ns, name), &operatorkubearmorcomv1.KubeArmorConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*operatorkubearmorcomv1.KubeArmorConfig), err +} + +// List takes label and field selectors, and returns the list of KubeArmorConfigs that match those selectors. +func (c *FakeKubeArmorConfigs) List(ctx context.Context, opts v1.ListOptions) (result *operatorkubearmorcomv1.KubeArmorConfigList, err error) { + obj, err := c.Fake. + Invokes(testing.NewListAction(kubearmorconfigsResource, kubearmorconfigsKind, c.ns, opts), &operatorkubearmorcomv1.KubeArmorConfigList{}) + + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &operatorkubearmorcomv1.KubeArmorConfigList{ListMeta: obj.(*operatorkubearmorcomv1.KubeArmorConfigList).ListMeta} + for _, item := range obj.(*operatorkubearmorcomv1.KubeArmorConfigList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested kubeArmorConfigs. +func (c *FakeKubeArmorConfigs) Watch(ctx context.Context, opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewWatchAction(kubearmorconfigsResource, c.ns, opts)) + +} + +// Create takes the representation of a kubeArmorConfig and creates it. Returns the server's representation of the kubeArmorConfig, and an error, if there is any. +func (c *FakeKubeArmorConfigs) Create(ctx context.Context, kubeArmorConfig *operatorkubearmorcomv1.KubeArmorConfig, opts v1.CreateOptions) (result *operatorkubearmorcomv1.KubeArmorConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewCreateAction(kubearmorconfigsResource, c.ns, kubeArmorConfig), &operatorkubearmorcomv1.KubeArmorConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*operatorkubearmorcomv1.KubeArmorConfig), err +} + +// Update takes the representation of a kubeArmorConfig and updates it. Returns the server's representation of the kubeArmorConfig, and an error, if there is any. +func (c *FakeKubeArmorConfigs) Update(ctx context.Context, kubeArmorConfig *operatorkubearmorcomv1.KubeArmorConfig, opts v1.UpdateOptions) (result *operatorkubearmorcomv1.KubeArmorConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateAction(kubearmorconfigsResource, c.ns, kubeArmorConfig), &operatorkubearmorcomv1.KubeArmorConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*operatorkubearmorcomv1.KubeArmorConfig), err +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *FakeKubeArmorConfigs) UpdateStatus(ctx context.Context, kubeArmorConfig *operatorkubearmorcomv1.KubeArmorConfig, opts v1.UpdateOptions) (*operatorkubearmorcomv1.KubeArmorConfig, error) { + obj, err := c.Fake. + Invokes(testing.NewUpdateSubresourceAction(kubearmorconfigsResource, "status", c.ns, kubeArmorConfig), &operatorkubearmorcomv1.KubeArmorConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*operatorkubearmorcomv1.KubeArmorConfig), err +} + +// Delete takes name of the kubeArmorConfig and deletes it. Returns an error if one occurs. +func (c *FakeKubeArmorConfigs) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewDeleteAction(kubearmorconfigsResource, c.ns, name), &operatorkubearmorcomv1.KubeArmorConfig{}) + + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeKubeArmorConfigs) DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error { + action := testing.NewDeleteCollectionAction(kubearmorconfigsResource, c.ns, listOpts) + + _, err := c.Fake.Invokes(action, &operatorkubearmorcomv1.KubeArmorConfigList{}) + return err +} + +// Patch applies the patch and returns the patched kubeArmorConfig. +func (c *FakeKubeArmorConfigs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts v1.PatchOptions, subresources ...string) (result *operatorkubearmorcomv1.KubeArmorConfig, err error) { + obj, err := c.Fake. + Invokes(testing.NewPatchSubresourceAction(kubearmorconfigsResource, c.ns, name, pt, data, subresources...), &operatorkubearmorcomv1.KubeArmorConfig{}) + + if obj == nil { + return nil, err + } + return obj.(*operatorkubearmorcomv1.KubeArmorConfig), err +} diff --git a/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/fake/fake_operator.kubearmor.com_client.go b/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/fake/fake_operator.kubearmor.com_client.go new file mode 100644 index 0000000000..c903399461 --- /dev/null +++ b/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/fake/fake_operator.kubearmor.com_client.go @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1" + rest "k8s.io/client-go/rest" + testing "k8s.io/client-go/testing" +) + +type FakeOperatorV1 struct { + *testing.Fake +} + +func (c *FakeOperatorV1) KubeArmorConfigs(namespace string) v1.KubeArmorConfigInterface { + return &FakeKubeArmorConfigs{c, namespace} +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *FakeOperatorV1) RESTClient() rest.Interface { + var ret *rest.RESTClient + return ret +} diff --git a/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/generated_expansion.go b/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/generated_expansion.go new file mode 100644 index 0000000000..7304bcff53 --- /dev/null +++ b/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/generated_expansion.go @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +type KubeArmorConfigExpansion interface{} diff --git a/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/kubearmorconfig.go b/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/kubearmorconfig.go new file mode 100644 index 0000000000..5fbfb0dc85 --- /dev/null +++ b/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/kubearmorconfig.go @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + "context" + "time" + + v1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1" + scheme "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/clientset/versioned/scheme" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// KubeArmorConfigsGetter has a method to return a KubeArmorConfigInterface. +// A group's client should implement this interface. +type KubeArmorConfigsGetter interface { + KubeArmorConfigs(namespace string) KubeArmorConfigInterface +} + +// KubeArmorConfigInterface has methods to work with KubeArmorConfig resources. +type KubeArmorConfigInterface interface { + Create(ctx context.Context, kubeArmorConfig *v1.KubeArmorConfig, opts metav1.CreateOptions) (*v1.KubeArmorConfig, error) + Update(ctx context.Context, kubeArmorConfig *v1.KubeArmorConfig, opts metav1.UpdateOptions) (*v1.KubeArmorConfig, error) + UpdateStatus(ctx context.Context, kubeArmorConfig *v1.KubeArmorConfig, opts metav1.UpdateOptions) (*v1.KubeArmorConfig, error) + Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error + DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error + Get(ctx context.Context, name string, opts metav1.GetOptions) (*v1.KubeArmorConfig, error) + List(ctx context.Context, opts metav1.ListOptions) (*v1.KubeArmorConfigList, error) + Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) + Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.KubeArmorConfig, err error) + KubeArmorConfigExpansion +} + +// kubeArmorConfigs implements KubeArmorConfigInterface +type kubeArmorConfigs struct { + client rest.Interface + ns string +} + +// newKubeArmorConfigs returns a KubeArmorConfigs +func newKubeArmorConfigs(c *OperatorV1Client, namespace string) *kubeArmorConfigs { + return &kubeArmorConfigs{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the kubeArmorConfig, and returns the corresponding kubeArmorConfig object, and an error if there is any. +func (c *kubeArmorConfigs) Get(ctx context.Context, name string, options metav1.GetOptions) (result *v1.KubeArmorConfig, err error) { + result = &v1.KubeArmorConfig{} + err = c.client.Get(). + Namespace(c.ns). + Resource("kubearmorconfigs"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(ctx). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of KubeArmorConfigs that match those selectors. +func (c *kubeArmorConfigs) List(ctx context.Context, opts metav1.ListOptions) (result *v1.KubeArmorConfigList, err error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + result = &v1.KubeArmorConfigList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("kubearmorconfigs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Do(ctx). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested kubeArmorConfigs. +func (c *kubeArmorConfigs) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) { + var timeout time.Duration + if opts.TimeoutSeconds != nil { + timeout = time.Duration(*opts.TimeoutSeconds) * time.Second + } + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("kubearmorconfigs"). + VersionedParams(&opts, scheme.ParameterCodec). + Timeout(timeout). + Watch(ctx) +} + +// Create takes the representation of a kubeArmorConfig and creates it. Returns the server's representation of the kubeArmorConfig, and an error, if there is any. +func (c *kubeArmorConfigs) Create(ctx context.Context, kubeArmorConfig *v1.KubeArmorConfig, opts metav1.CreateOptions) (result *v1.KubeArmorConfig, err error) { + result = &v1.KubeArmorConfig{} + err = c.client.Post(). + Namespace(c.ns). + Resource("kubearmorconfigs"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(kubeArmorConfig). + Do(ctx). + Into(result) + return +} + +// Update takes the representation of a kubeArmorConfig and updates it. Returns the server's representation of the kubeArmorConfig, and an error, if there is any. +func (c *kubeArmorConfigs) Update(ctx context.Context, kubeArmorConfig *v1.KubeArmorConfig, opts metav1.UpdateOptions) (result *v1.KubeArmorConfig, err error) { + result = &v1.KubeArmorConfig{} + err = c.client.Put(). + Namespace(c.ns). + Resource("kubearmorconfigs"). + Name(kubeArmorConfig.Name). + VersionedParams(&opts, scheme.ParameterCodec). + Body(kubeArmorConfig). + Do(ctx). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). +func (c *kubeArmorConfigs) UpdateStatus(ctx context.Context, kubeArmorConfig *v1.KubeArmorConfig, opts metav1.UpdateOptions) (result *v1.KubeArmorConfig, err error) { + result = &v1.KubeArmorConfig{} + err = c.client.Put(). + Namespace(c.ns). + Resource("kubearmorconfigs"). + Name(kubeArmorConfig.Name). + SubResource("status"). + VersionedParams(&opts, scheme.ParameterCodec). + Body(kubeArmorConfig). + Do(ctx). + Into(result) + return +} + +// Delete takes name of the kubeArmorConfig and deletes it. Returns an error if one occurs. +func (c *kubeArmorConfigs) Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("kubearmorconfigs"). + Name(name). + Body(&opts). + Do(ctx). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *kubeArmorConfigs) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error { + var timeout time.Duration + if listOpts.TimeoutSeconds != nil { + timeout = time.Duration(*listOpts.TimeoutSeconds) * time.Second + } + return c.client.Delete(). + Namespace(c.ns). + Resource("kubearmorconfigs"). + VersionedParams(&listOpts, scheme.ParameterCodec). + Timeout(timeout). + Body(&opts). + Do(ctx). + Error() +} + +// Patch applies the patch and returns the patched kubeArmorConfig. +func (c *kubeArmorConfigs) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *v1.KubeArmorConfig, err error) { + result = &v1.KubeArmorConfig{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("kubearmorconfigs"). + Name(name). + SubResource(subresources...). + VersionedParams(&opts, scheme.ParameterCodec). + Body(data). + Do(ctx). + Into(result) + return +} diff --git a/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/operator.kubearmor.com_client.go b/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/operator.kubearmor.com_client.go new file mode 100644 index 0000000000..c129d00b65 --- /dev/null +++ b/pkg/KubeArmorOperator/client/clientset/versioned/typed/operator.kubearmor.com/v1/operator.kubearmor.com_client.go @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by client-gen. DO NOT EDIT. + +package v1 + +import ( + v1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1" + "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/clientset/versioned/scheme" + rest "k8s.io/client-go/rest" +) + +type OperatorV1Interface interface { + RESTClient() rest.Interface + KubeArmorConfigsGetter +} + +// OperatorV1Client is used to interact with features provided by the operator.kubearmor.com group. +type OperatorV1Client struct { + restClient rest.Interface +} + +func (c *OperatorV1Client) KubeArmorConfigs(namespace string) KubeArmorConfigInterface { + return newKubeArmorConfigs(c, namespace) +} + +// NewForConfig creates a new OperatorV1Client for the given config. +func NewForConfig(c *rest.Config) (*OperatorV1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &OperatorV1Client{client}, nil +} + +// NewForConfigOrDie creates a new OperatorV1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *OperatorV1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new OperatorV1Client for the given RESTClient. +func New(c rest.Interface) *OperatorV1Client { + return &OperatorV1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *OperatorV1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/pkg/KubeArmorOperator/client/informers/externalversions/factory.go b/pkg/KubeArmorOperator/client/informers/externalversions/factory.go new file mode 100644 index 0000000000..be10eba7a3 --- /dev/null +++ b/pkg/KubeArmorOperator/client/informers/externalversions/factory.go @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + reflect "reflect" + sync "sync" + time "time" + + versioned "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/clientset/versioned" + internalinterfaces "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/informers/externalversions/internalinterfaces" + operatorkubearmorcom "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/informers/externalversions/operator.kubearmor.com" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// SharedInformerOption defines the functional option type for SharedInformerFactory. +type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory + +type sharedInformerFactory struct { + client versioned.Interface + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc + lock sync.Mutex + defaultResync time.Duration + customResync map[reflect.Type]time.Duration + + informers map[reflect.Type]cache.SharedIndexInformer + // startedInformers is used for tracking which informers have been started. + // This allows Start() to be called multiple times safely. + startedInformers map[reflect.Type]bool +} + +// WithCustomResyncConfig sets a custom resync period for the specified informer types. +func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + for k, v := range resyncConfig { + factory.customResync[reflect.TypeOf(k)] = v + } + return factory + } +} + +// WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. +func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.tweakListOptions = tweakListOptions + return factory + } +} + +// WithNamespace limits the SharedInformerFactory to the specified namespace. +func WithNamespace(namespace string) SharedInformerOption { + return func(factory *sharedInformerFactory) *sharedInformerFactory { + factory.namespace = namespace + return factory + } +} + +// NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. +func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync) +} + +// NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. +// Listers obtained via this SharedInformerFactory will be subject to the same filters +// as specified here. +// Deprecated: Please use NewSharedInformerFactoryWithOptions instead +func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { + return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) +} + +// NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. +func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { + factory := &sharedInformerFactory{ + client: client, + namespace: v1.NamespaceAll, + defaultResync: defaultResync, + informers: make(map[reflect.Type]cache.SharedIndexInformer), + startedInformers: make(map[reflect.Type]bool), + customResync: make(map[reflect.Type]time.Duration), + } + + // Apply all options + for _, opt := range options { + factory = opt(factory) + } + + return factory +} + +// Start initializes all requested informers. +func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { + f.lock.Lock() + defer f.lock.Unlock() + + for informerType, informer := range f.informers { + if !f.startedInformers[informerType] { + go informer.Run(stopCh) + f.startedInformers[informerType] = true + } + } +} + +// WaitForCacheSync waits for all started informers' cache were synced. +func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { + informers := func() map[reflect.Type]cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informers := map[reflect.Type]cache.SharedIndexInformer{} + for informerType, informer := range f.informers { + if f.startedInformers[informerType] { + informers[informerType] = informer + } + } + return informers + }() + + res := map[reflect.Type]bool{} + for informType, informer := range informers { + res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) + } + return res +} + +// InternalInformerFor returns the SharedIndexInformer for obj using an internal +// client. +func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { + f.lock.Lock() + defer f.lock.Unlock() + + informerType := reflect.TypeOf(obj) + informer, exists := f.informers[informerType] + if exists { + return informer + } + + resyncPeriod, exists := f.customResync[informerType] + if !exists { + resyncPeriod = f.defaultResync + } + + informer = newFunc(f.client, resyncPeriod) + f.informers[informerType] = informer + + return informer +} + +// SharedInformerFactory provides shared informers for resources in all known +// API group versions. +type SharedInformerFactory interface { + internalinterfaces.SharedInformerFactory + ForResource(resource schema.GroupVersionResource) (GenericInformer, error) + WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool + + Operator() operatorkubearmorcom.Interface +} + +func (f *sharedInformerFactory) Operator() operatorkubearmorcom.Interface { + return operatorkubearmorcom.New(f, f.namespace, f.tweakListOptions) +} diff --git a/pkg/KubeArmorOperator/client/informers/externalversions/generic.go b/pkg/KubeArmorOperator/client/informers/externalversions/generic.go new file mode 100644 index 0000000000..fd79d3b1a9 --- /dev/null +++ b/pkg/KubeArmorOperator/client/informers/externalversions/generic.go @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by informer-gen. DO NOT EDIT. + +package externalversions + +import ( + "fmt" + + v1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1" + schema "k8s.io/apimachinery/pkg/runtime/schema" + cache "k8s.io/client-go/tools/cache" +) + +// GenericInformer is type of SharedIndexInformer which will locate and delegate to other +// sharedInformers based on type +type GenericInformer interface { + Informer() cache.SharedIndexInformer + Lister() cache.GenericLister +} + +type genericInformer struct { + informer cache.SharedIndexInformer + resource schema.GroupResource +} + +// Informer returns the SharedIndexInformer. +func (f *genericInformer) Informer() cache.SharedIndexInformer { + return f.informer +} + +// Lister returns the GenericLister. +func (f *genericInformer) Lister() cache.GenericLister { + return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) +} + +// ForResource gives generic access to a shared informer of the matching type +// TODO extend this to unknown resources with a client pool +func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { + switch resource { + // Group=operator.kubearmor.com, Version=v1 + case v1.SchemeGroupVersion.WithResource("kubearmorconfigs"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Operator().V1().KubeArmorConfigs().Informer()}, nil + + } + + return nil, fmt.Errorf("no informer found for %v", resource) +} diff --git a/pkg/KubeArmorOperator/client/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/KubeArmorOperator/client/informers/externalversions/internalinterfaces/factory_interfaces.go new file mode 100644 index 0000000000..ddae13ea35 --- /dev/null +++ b/pkg/KubeArmorOperator/client/informers/externalversions/internalinterfaces/factory_interfaces.go @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by informer-gen. DO NOT EDIT. + +package internalinterfaces + +import ( + time "time" + + versioned "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/clientset/versioned" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + cache "k8s.io/client-go/tools/cache" +) + +// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. +type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer + +// SharedInformerFactory a small interface to allow for adding an informer without an import cycle +type SharedInformerFactory interface { + Start(stopCh <-chan struct{}) + InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer +} + +// TweakListOptionsFunc is a function that transforms a v1.ListOptions. +type TweakListOptionsFunc func(*v1.ListOptions) diff --git a/pkg/KubeArmorOperator/client/informers/externalversions/operator.kubearmor.com/interface.go b/pkg/KubeArmorOperator/client/informers/externalversions/operator.kubearmor.com/interface.go new file mode 100644 index 0000000000..28727a019a --- /dev/null +++ b/pkg/KubeArmorOperator/client/informers/externalversions/operator.kubearmor.com/interface.go @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by informer-gen. DO NOT EDIT. + +package operator + +import ( + internalinterfaces "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/informers/externalversions/internalinterfaces" + v1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/informers/externalversions/operator.kubearmor.com/v1" +) + +// Interface provides access to each of this group's versions. +type Interface interface { + // V1 provides access to shared informers for resources in V1. + V1() v1.Interface +} + +type group struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// V1 returns a new v1.Interface. +func (g *group) V1() v1.Interface { + return v1.New(g.factory, g.namespace, g.tweakListOptions) +} diff --git a/pkg/KubeArmorOperator/client/informers/externalversions/operator.kubearmor.com/v1/interface.go b/pkg/KubeArmorOperator/client/informers/externalversions/operator.kubearmor.com/v1/interface.go new file mode 100644 index 0000000000..ca96173bda --- /dev/null +++ b/pkg/KubeArmorOperator/client/informers/externalversions/operator.kubearmor.com/v1/interface.go @@ -0,0 +1,32 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by informer-gen. DO NOT EDIT. + +package v1 + +import ( + internalinterfaces "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/informers/externalversions/internalinterfaces" +) + +// Interface provides access to all the informers in this group version. +type Interface interface { + // KubeArmorConfigs returns a KubeArmorConfigInformer. + KubeArmorConfigs() KubeArmorConfigInformer +} + +type version struct { + factory internalinterfaces.SharedInformerFactory + namespace string + tweakListOptions internalinterfaces.TweakListOptionsFunc +} + +// New returns a new Interface. +func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { + return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} +} + +// KubeArmorConfigs returns a KubeArmorConfigInformer. +func (v *version) KubeArmorConfigs() KubeArmorConfigInformer { + return &kubeArmorConfigInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} +} diff --git a/pkg/KubeArmorOperator/client/informers/externalversions/operator.kubearmor.com/v1/kubearmorconfig.go b/pkg/KubeArmorOperator/client/informers/externalversions/operator.kubearmor.com/v1/kubearmorconfig.go new file mode 100644 index 0000000000..2d6d110b2d --- /dev/null +++ b/pkg/KubeArmorOperator/client/informers/externalversions/operator.kubearmor.com/v1/kubearmorconfig.go @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by informer-gen. DO NOT EDIT. + +package v1 + +import ( + "context" + time "time" + + operatorkubearmorcomv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1" + versioned "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/clientset/versioned" + internalinterfaces "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/informers/externalversions/internalinterfaces" + v1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/listers/operator.kubearmor.com/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + watch "k8s.io/apimachinery/pkg/watch" + cache "k8s.io/client-go/tools/cache" +) + +// KubeArmorConfigInformer provides access to a shared informer and lister for +// KubeArmorConfigs. +type KubeArmorConfigInformer interface { + Informer() cache.SharedIndexInformer + Lister() v1.KubeArmorConfigLister +} + +type kubeArmorConfigInformer struct { + factory internalinterfaces.SharedInformerFactory + tweakListOptions internalinterfaces.TweakListOptionsFunc + namespace string +} + +// NewKubeArmorConfigInformer constructs a new informer for KubeArmorConfig type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewKubeArmorConfigInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredKubeArmorConfigInformer(client, namespace, resyncPeriod, indexers, nil) +} + +// NewFilteredKubeArmorConfigInformer constructs a new informer for KubeArmorConfig type. +// Always prefer using an informer factory to get a shared informer instead of getting an independent +// one. This reduces memory footprint and number of connections to the server. +func NewFilteredKubeArmorConfigInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { + return cache.NewSharedIndexInformer( + &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.OperatorV1().KubeArmorConfigs(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + if tweakListOptions != nil { + tweakListOptions(&options) + } + return client.OperatorV1().KubeArmorConfigs(namespace).Watch(context.TODO(), options) + }, + }, + &operatorkubearmorcomv1.KubeArmorConfig{}, + resyncPeriod, + indexers, + ) +} + +func (f *kubeArmorConfigInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredKubeArmorConfigInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +} + +func (f *kubeArmorConfigInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&operatorkubearmorcomv1.KubeArmorConfig{}, f.defaultInformer) +} + +func (f *kubeArmorConfigInformer) Lister() v1.KubeArmorConfigLister { + return v1.NewKubeArmorConfigLister(f.Informer().GetIndexer()) +} diff --git a/pkg/KubeArmorOperator/client/listers/operator.kubearmor.com/v1/expansion_generated.go b/pkg/KubeArmorOperator/client/listers/operator.kubearmor.com/v1/expansion_generated.go new file mode 100644 index 0000000000..c8dddbcec4 --- /dev/null +++ b/pkg/KubeArmorOperator/client/listers/operator.kubearmor.com/v1/expansion_generated.go @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by lister-gen. DO NOT EDIT. + +package v1 + +// KubeArmorConfigListerExpansion allows custom methods to be added to +// KubeArmorConfigLister. +type KubeArmorConfigListerExpansion interface{} + +// KubeArmorConfigNamespaceListerExpansion allows custom methods to be added to +// KubeArmorConfigNamespaceLister. +type KubeArmorConfigNamespaceListerExpansion interface{} diff --git a/pkg/KubeArmorOperator/client/listers/operator.kubearmor.com/v1/kubearmorconfig.go b/pkg/KubeArmorOperator/client/listers/operator.kubearmor.com/v1/kubearmorconfig.go new file mode 100644 index 0000000000..3953d81197 --- /dev/null +++ b/pkg/KubeArmorOperator/client/listers/operator.kubearmor.com/v1/kubearmorconfig.go @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor + +// Code generated by lister-gen. DO NOT EDIT. + +package v1 + +import ( + v1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// KubeArmorConfigLister helps list KubeArmorConfigs. +// All objects returned here must be treated as read-only. +type KubeArmorConfigLister interface { + // List lists all KubeArmorConfigs in the indexer. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1.KubeArmorConfig, err error) + // KubeArmorConfigs returns an object that can list and get KubeArmorConfigs. + KubeArmorConfigs(namespace string) KubeArmorConfigNamespaceLister + KubeArmorConfigListerExpansion +} + +// kubeArmorConfigLister implements the KubeArmorConfigLister interface. +type kubeArmorConfigLister struct { + indexer cache.Indexer +} + +// NewKubeArmorConfigLister returns a new KubeArmorConfigLister. +func NewKubeArmorConfigLister(indexer cache.Indexer) KubeArmorConfigLister { + return &kubeArmorConfigLister{indexer: indexer} +} + +// List lists all KubeArmorConfigs in the indexer. +func (s *kubeArmorConfigLister) List(selector labels.Selector) (ret []*v1.KubeArmorConfig, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1.KubeArmorConfig)) + }) + return ret, err +} + +// KubeArmorConfigs returns an object that can list and get KubeArmorConfigs. +func (s *kubeArmorConfigLister) KubeArmorConfigs(namespace string) KubeArmorConfigNamespaceLister { + return kubeArmorConfigNamespaceLister{indexer: s.indexer, namespace: namespace} +} + +// KubeArmorConfigNamespaceLister helps list and get KubeArmorConfigs. +// All objects returned here must be treated as read-only. +type KubeArmorConfigNamespaceLister interface { + // List lists all KubeArmorConfigs in the indexer for a given namespace. + // Objects returned here must be treated as read-only. + List(selector labels.Selector) (ret []*v1.KubeArmorConfig, err error) + // Get retrieves the KubeArmorConfig from the indexer for a given namespace and name. + // Objects returned here must be treated as read-only. + Get(name string) (*v1.KubeArmorConfig, error) + KubeArmorConfigNamespaceListerExpansion +} + +// kubeArmorConfigNamespaceLister implements the KubeArmorConfigNamespaceLister +// interface. +type kubeArmorConfigNamespaceLister struct { + indexer cache.Indexer + namespace string +} + +// List lists all KubeArmorConfigs in the indexer for a given namespace. +func (s kubeArmorConfigNamespaceLister) List(selector labels.Selector) (ret []*v1.KubeArmorConfig, err error) { + err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { + ret = append(ret, m.(*v1.KubeArmorConfig)) + }) + return ret, err +} + +// Get retrieves the KubeArmorConfig from the indexer for a given namespace and name. +func (s kubeArmorConfigNamespaceLister) Get(name string) (*v1.KubeArmorConfig, error) { + obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1.Resource("kubearmorconfig"), name) + } + return obj.(*v1.KubeArmorConfig), nil +} diff --git a/pkg/KubeArmorOperator/cmd/main.go b/pkg/KubeArmorOperator/cmd/main.go new file mode 100644 index 0000000000..5ae1f99fa9 --- /dev/null +++ b/pkg/KubeArmorOperator/cmd/main.go @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Authors of KubeArmor + +package main + +import ( + "os" + + operator "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/cmd/operator" + snitch "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/cmd/snitch-cmd" + "github.com/spf13/cobra" + "go.uber.org/zap" +) + +var Logger *zap.SugaredLogger + +var rootCmd = &cobra.Command{ + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + log, err := zap.NewProduction() + if err != nil { + return err + } + Logger = log.Sugar() + return nil + }, + Use: "operator", + Short: "A CLI utility to install kubearmor-operator or snitch on k8s cluster", + Long: "A CLI utility to install kubearmor-operator or snitch on k8s cluster", +} + +func Execute() { + if err := rootCmd.Execute(); err != nil { + Logger.Error(err) + os.Exit(1) + } +} + +func main() { + Execute() +} + +func init() { + rootCmd.AddCommand(snitch.Cmd) + rootCmd.AddCommand(operator.Cmd) +} diff --git a/pkg/KubeArmorOperator/cmd/operator/root.go b/pkg/KubeArmorOperator/cmd/operator/root.go new file mode 100644 index 0000000000..10090949fb --- /dev/null +++ b/pkg/KubeArmorOperator/cmd/operator/root.go @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2021 Authors of KubeArmor + +// Package cmd is the collection of all the subcommands available in kArmor while providing relevant options for the same +package operator + +import ( + "errors" + "path/filepath" + + opv1client "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/clientset/versioned" + controllers "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/internal/controller" + "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/k8s" + "github.com/spf13/cobra" + "go.uber.org/zap" + apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/util/homedir" +) + +var K8sClient *kubernetes.Clientset +var Logger *zap.SugaredLogger +var KubeConfig string +var Context string +var LsmOrder string +var PathPrefix string +var DeploymentName string +var ExtClient *apiextensionsclientset.Clientset +var Opv1Client *opv1client.Clientset + +// rootCmd represents the base command when called without any subcommands +var Cmd = &cobra.Command{ + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + log, _ := zap.NewProduction() + Logger = log.Sugar() + K8sClient = k8s.NewClient(*Logger, KubeConfig) + ExtClient = k8s.NewExtClient(*Logger, KubeConfig) + Opv1Client = k8s.NewOpv1Client(*Logger, KubeConfig) + //Initialise k8sClient for all child commands to inherit + if K8sClient == nil { + return errors.New("could'nt create k8s client") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + nodeWatcher := controllers.NewClusterWatcher(K8sClient, Logger, ExtClient, Opv1Client, PathPrefix, DeploymentName) + go nodeWatcher.WatchConfigCrd() + nodeWatcher.WatchNodes() + + }, + Use: "kubearmor-operator", + Short: "An operator to install kubearmor on k8s clusters", + Long: `An operator to install kubearmor on k8s clusters + +KubeArmor is a container-aware runtime security enforcement system that +restricts the behavior (such as process execution, file access, and networking +operation) of containers at the system level. + `, + SilenceUsage: true, + SilenceErrors: true, +} + +func init() { + if home := homedir.HomeDir(); home != "" { + Cmd.PersistentFlags().StringVar(&KubeConfig, "kubeconfig", filepath.Join(home, ".kube", "config"), "Path to the kubeconfig file to use") + } else { + Cmd.PersistentFlags().StringVar(&KubeConfig, "kubeconfig", "", "Path to the kubeconfig file to use") + } + Cmd.PersistentFlags().StringVar(&LsmOrder, "lsm", "bpf,apparmor,selinux", "lsm preference order to use") + Cmd.PersistentFlags().StringVar(&PathPrefix, "pathprefix", "/rootfs/", "path prefix for runtime search") + Cmd.PersistentFlags().StringVar(&DeploymentName, "deploymentName", "kubearmor-operator", "operator deployment name") +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + cobra.CheckErr(Cmd.Execute()) +} diff --git a/pkg/KubeArmorOperator/cmd/snitch-cmd/root.go b/pkg/KubeArmorOperator/cmd/snitch-cmd/root.go new file mode 100644 index 0000000000..ec76f348f1 --- /dev/null +++ b/pkg/KubeArmorOperator/cmd/snitch-cmd/root.go @@ -0,0 +1,146 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2021 Authors of KubeArmor + +// Package cmd is the collection of all the subcommands available in kArmor while providing relevant options for the same +package snitch + +import ( + "context" + "encoding/json" + "errors" + "os" + "path/filepath" + "strings" + + "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/common" + "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/enforcer" + "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/k8s" + runtimepkg "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/runtime" + "github.com/spf13/cobra" + "go.uber.org/zap" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/rand" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/util/homedir" +) + +type metadata struct { + Metadata metadataSpec `json:"metadata"` +} + +type metadataSpec struct { + Labels map[string]string `json:"labels"` +} + +var K8sClient *kubernetes.Clientset +var Logger *zap.SugaredLogger +var KubeConfig string +var Context string +var LsmOrder string +var PathPrefix string = "/rootfs" +var NodeName string +var Runtime string + +// Cmd represents the base command when called without any subcommands +var Cmd = &cobra.Command{ + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + log, _ := zap.NewProduction() + Logger = log.Sugar() + K8sClient = k8s.NewClient(*Logger, KubeConfig) + //Initialise k8sClient for all child commands to inherit + if K8sClient == nil { + return errors.New("could'nt create k8s client") + } + return nil + }, + Run: func(cmd *cobra.Command, args []string) { + Logger.Infof("Running snitch in node %s", NodeName) + Logger.Infof("lsm order=%s", LsmOrder) + Logger.Infof("path prefix=%s", PathPrefix) + Logger.Infof("k8s runtime=%s", Runtime) + Logger.Infof("KubeConfig path=%s", KubeConfig) + snitch() + + }, + Use: "snitch", + Short: "A CLI Utility to Detect node related informations for KubeArmor", + Long: `CLI Utility to Detect node related informations for KubeArmor + +KubeArmor is a container-aware runtime security enforcement system that +restricts the behavior (such as process execution, file access, and networking +operation) of containers at the system level. + `, + SilenceUsage: true, + SilenceErrors: true, +} + +func init() { + if home := homedir.HomeDir(); home != "" { + Cmd.PersistentFlags().StringVar(&KubeConfig, "kubeconfig", filepath.Join(home, ".kube", "config"), "Path to the kubeconfig file to use") + } else { + Cmd.PersistentFlags().StringVar(&KubeConfig, "kubeconfig", "", "Path to the kubeconfig file to use") + } + Cmd.PersistentFlags().StringVar(&LsmOrder, "lsm", "bpf,apparmor,selinux", "lsm preference order to use") + Cmd.PersistentFlags().StringVar(&NodeName, "nodename", "", "node name to label") + Cmd.PersistentFlags().StringVar(&PathPrefix, "pathprefix", "/rootfs", "path prefix for runtime search") + Cmd.PersistentFlags().StringVar(&Runtime, "runtime", "", "runtime detected by k8s") +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the Cmd. +func Execute() { + cobra.CheckErr(Cmd.Execute()) +} + +func snitch() { + order := strings.Split(LsmOrder, ",") + + // Detecting enforcer + nodeEnforcer := enforcer.DetectEnforcer(order, PathPrefix, *Logger) + if nodeEnforcer != "NA" { + Logger.Infof("Node enforcer is %s", nodeEnforcer) + } else { + Logger.Info("Node doesn't supports any KubeArmor Supported Lsm, Enforcement is disabled") + nodeEnforcer = "none" + } + + //Detecting runtime + + runtime, socket := runtimepkg.DetectRuntimeViaMap(PathPrefix, Runtime, *Logger) + if runtime != "NA" { + Logger.Infof("Detected %s as node runtime, runtime socket=%s", runtime, socket) + } else { + // don't throw an error instead print info that no lsm is present + Logger.Errorf("Not able to runtime") + os.Exit(1) + } + runtimeStorage := runtimepkg.DetectRuntimeStorage(PathPrefix, runtime, *Logger) + if runtimeStorage != "NA" { + Logger.Infof("Detected runtime storage location %s", runtimeStorage) + } else { + Logger.Errorf("Not able to detect runtime storage location") + os.Exit(1) + } + + patchNode := metadata{} + patchNode.Metadata.Labels = map[string]string{} + patchNode.Metadata.Labels[common.RuntimeLabel] = runtime + patchNode.Metadata.Labels[common.SocketLabel] = strings.ReplaceAll(socket[1:], "/", "_") + patchNode.Metadata.Labels[common.EnforcerLabel] = nodeEnforcer + patchNode.Metadata.Labels[common.RuntimeStorageLabel] = strings.ReplaceAll(runtimeStorage[1:], "/", "_") + patchNode.Metadata.Labels[common.RandLabel] = rand.String(4) + patch, err := json.Marshal(patchNode) + + if err != nil { + Logger.Errorf("Error while marshaling json, error=%s", err.Error()) + os.Exit(1) + } + _, err = K8sClient.CoreV1().Nodes().Patch(context.Background(), NodeName, types.MergePatchType, patch, v1.PatchOptions{}) + if err != nil { + Logger.Errorf("Error while patching node %s error=%s", NodeName, err.Error()) + os.Exit(1) + } else { + Logger.Infof("Patched node %s, patch=%s", NodeName, string(patch)) + } +} diff --git a/pkg/KubeArmorOperator/common/defaults.go b/pkg/KubeArmorOperator/common/defaults.go new file mode 100644 index 0000000000..0d47a91ca2 --- /dev/null +++ b/pkg/KubeArmorOperator/common/defaults.go @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Authors of KubeArmor + +package common + +import ( + "context" + "crypto/sha512" + "encoding/hex" + "os" + "strings" + + deployments "github.com/kubearmor/KubeArmor/deployments/get" + opv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/rand" + "k8s.io/client-go/kubernetes" +) + +const ( + // constants for CRD status + CREATED string = "Created" + PENDING string = "Pending" + RUNNING string = "Running" + UPDATING string = "Updating" + ERROR string = "Error" + + // Status Messages + CREATED_MSG string = "Installaltion has been created" + PENDING_MSG string = "Kubearmor Installation is in-progess" + RUNNING_MSG string = "Kubearmor Application is Up and Running" + UPDATING_MSG string = "Updating the Application Configuration" + + // Error Messages + INSTALLATION_ERR_MSG string = "Failed to install KubeArmor component(s)" + MULTIPLE_CRD_ERR_MSG string = "There's already a CRD exists to manage KubeArmor" + UPDATION_FAILED_ERR_MSG string = "Failed to update KubeArmor configuration" +) + +var OperatigConfigCrd *opv1.KubeArmorConfig + +var ( + EnforcerLabel string = "kubearmor.io/enforcer" + RuntimeLabel string = "kubearmor.io/runtime" + RuntimeStorageLabel string = "kubearmor.io/runtime-storage" + SocketLabel string = "kubearmor.io/socket" + RandLabel string = "kubearmor.io/rand" + OsLabel string = "kubernetes.io/os" + ArchLabel string = "kubernetes.io/arch" + DeletAction string = "DELETE" + AddAction string = "ADD" + Namespace string = "kube-system" + Privileged bool = true + OperatorName string = "kubearmor-operator" + OperatorImage string = "kubearmor/kubearmor-operator:latest" + KubeArmorServiceAccountName string = "kubearmor" + KubeArmorClusterRoleBindingName string = KubeArmorServiceAccountName + KubeArmorSnitchRoleName string = "kubearmor-snitch" + + // KubeArmorConfigMapName string = "kubearmor-config" + + // ConfigMap Data + ConfigGRPC string = "gRPC" + ConfigVisibility string = "visibility" + ConfigCluster string = "cluster" + ConfigDefaultFilePosture string = "defaultFilePosture" + ConfigDefaultCapabilitiesPosture string = "defaultCapabilitiesPosture" + ConfigDefaultNetworkPosture string = "defaultNetworkPosture" + + // Images + KubeArmorImage string = "kubearmor/kubearmor:stable" + KubeArmorImagePullPolicy string = "Always" + KubeArmorInitImage string = "kubearmor/kubearmor-init:stable" + KubeArmorInitImagePullPolicy string = "Always" + KubeArmorRelayImage string = "kubearmor/kubearmor-relay-server:latest" + KubeArmorRelayImagePullPolicy string = "Always" + KubeArmorControllerImage string = "kubearmor/kubearmor-controller:latest" + KubeArmorControllerImagePullPolicy string = "Always" + KubeRbacProxyImage string = "gcr.io/kubebuilder/kube-rbac-proxy:v0.12.0" + KubeRbacProxyImagePullPolicy string = "Always" +) + +var ConfigMapData = map[string]string{ + ConfigGRPC: "32767", + ConfigCluster: "default", + ConfigDefaultFilePosture: "audit", + ConfigDefaultCapabilitiesPosture: "audit", + ConfigDefaultNetworkPosture: "audit", + ConfigVisibility: "process,file,network", +} + +var ContainerRuntimeSocketMap = map[string][]string{ + "docker": { + "/var/run/docker.sock", + "/run/docker.sock", + }, + "containerd": { + "/var/snap/microk8s/common/run/containerd.sock", + "/run/k3s/containerd/containerd.sock", + "/run/containerd/containerd.sock", + "/var/run/containerd/containerd.sock", + "/run/dockershim.sock", + }, + "cri-o": { + "/var/run/crio/crio.sock", + "/run/crio/crio.sock", + }, +} + +var HostPathDirectory = corev1.HostPathDirectory +var HostPathSocket = corev1.HostPathSocket +var HostPathFile = corev1.HostPathFile +var HostToContainerMountPropagation = corev1.MountPropagationHostToContainer + +var EnforcerVolumesMounts = map[string][]corev1.VolumeMount{ + "apparmor": { + { + Name: "etc-apparmor-d-path", + MountPath: "/etc/apparmor.d", + }, + }, + "bpf": { + { + Name: "sys-fs-bpf-path", + MountPath: "/sys/fs/bpf", + }, + }, +} + +var EnforcerVolumes = map[string][]corev1.Volume{ + "apparmor": { + { + Name: "etc-apparmor-d-path", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/etc/apparmor.d", + Type: &HostPathDirectory, + }, + }, + }, + }, + "bpf": { + + { + Name: "sys-fs-bpf-path", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/sys/fs/bpf", + Type: &HostPathDirectory, + }, + }, + }, + }, +} + +var RuntimeStorageVolumes = map[string][]string{ + "docker": { + "/var/lib/docker", + }, + "cri-o": { + "/var/lib/containers/storage", + }, + "containerd": { + "/run/k3s/containerd", + "/run/containerd", + }, +} + +func ShortSHA(s string) string { + sBytes := []byte(s) + + shaFunc := sha512.New() + shaFunc.Write(sBytes) + res := shaFunc.Sum(nil) + return hex.EncodeToString(res)[:5] +} + +var CommonVolumes = []corev1.Volume{ + { + Name: "bpf", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + { + Name: "sys-kernel-security-path", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/sys/kernel/security", + Type: &HostPathDirectory, + }, + }, + }, + { + Name: "sys-kernel-debug-path", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/sys/kernel/debug", + Type: &HostPathDirectory, + }, + }, + }, + { + Name: "os-release-path", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/etc/os-release", + Type: &HostPathFile, + }, + }, + }, +} + +var CommonVolumesMount = []corev1.VolumeMount{ + { + Name: "bpf", + MountPath: "/opt/kubearmor/BPF", + }, + { + Name: "sys-kernel-security-path", + MountPath: "/sys/kernel/security", + }, + { + Name: "sys-kernel-debug-path", + MountPath: "/sys/kernel/debug", + }, + { + Name: "os-release-path", + MountPath: "/media/root/etc/os-release", + ReadOnly: true, + }, +} + +var KernelHeaderVolumes = []corev1.Volume{ + { + Name: "lib-modules-path", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/lib/modules", + Type: &HostPathDirectory, + }, + }, + }, + { + Name: "usr-src-path", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/usr/src", + Type: &HostPathDirectory, + }, + }, + }, +} + +var KernelHeaderVolumesMount = []corev1.VolumeMount{ + { + Name: "usr-src-path", + MountPath: "/usr/src", + ReadOnly: true, + }, + { + Name: "lib-modules-path", + MountPath: "/lib/modules", + ReadOnly: true, + }, +} + +func GetFreeRandSuffix(c *kubernetes.Clientset, namespace string) (suffix string, err error) { + var found bool + for { + suffix = rand.String(5) + found = false + if _, err = c.CoreV1().Secrets(namespace).Get(context.Background(), deployments.KubeArmorControllerSecretName+"-"+suffix, metav1.GetOptions{}); err != nil { + if !strings.Contains(err.Error(), "not found") { + return "", err + } + } else { + found = true + } + + if !found { + break + } + } + return suffix, nil +} + +func GetOperatorNamespace() string { + ns := os.Getenv("KUBEARMOR_OPERATOR_NS") + + if ns == "" { + return Namespace + } + + return ns +} + +func CopyStrMap(src map[string]string) map[string]string { + newMap := make(map[string]string) + for key, value := range src { + newMap[key] = value + } + return newMap +} + +func init() { + Namespace = GetOperatorNamespace() +} diff --git a/pkg/KubeArmorOperator/common/tls.go b/pkg/KubeArmorOperator/common/tls.go new file mode 100644 index 0000000000..0446514afe --- /dev/null +++ b/pkg/KubeArmorOperator/common/tls.go @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Authors of KubeArmor + +package common + +import ( + "bytes" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "errors" + "math/big" + "time" +) + +// GeneratePki - generate pub/priv keypair +func GeneratePki(namespace string, serviceName string) (*bytes.Buffer, *bytes.Buffer, *bytes.Buffer, error) { + ca, cakey, err := GenerateCA() + if err != nil { + return bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}), err + } + csr, csrkey, err := GenerateCSR(namespace, serviceName) + if err != nil { + return bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}), err + } + crt, err := SignCSR(ca, cakey, csr, csrkey) + if err != nil { + return bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}), err + } + + caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &cakey.PublicKey, cakey) + if err != nil { + return bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}), err + } + caPEM := new(bytes.Buffer) + err = pem.Encode(caPEM, &pem.Block{ + Type: "CERTIFICATE", + Bytes: caBytes, + }) + if err != nil { + return bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}), err + } + crtPEM := new(bytes.Buffer) + err = pem.Encode(crtPEM, &pem.Block{ + Type: "CERTIFICATE", + Bytes: crt, + }) + if err != nil { + return bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}), err + } + crtKeyPEM := new(bytes.Buffer) + err = pem.Encode(crtKeyPEM, &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: x509.MarshalPKCS1PrivateKey(csrkey), + }) + if err != nil { + return bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}), bytes.NewBuffer([]byte{}), err + } + return caPEM, crtPEM, crtKeyPEM, nil +} + +// GenerateCA - generate private key and a cert for a CA +func GenerateCA() (*x509.Certificate, *rsa.PrivateKey, error) { + ca := &x509.Certificate{ + SerialNumber: big.NewInt(123), + Subject: pkix.Name{ + Organization: []string{"kubearmor"}, + Country: []string{"US"}, + Province: []string{""}, + CommonName: "kubearmor-ca", + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(3, 0, 0), + IsCA: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCRLSign | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + } + caPrivKey, err := rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + return &x509.Certificate{}, &rsa.PrivateKey{}, errors.New("cannot generate ca private key") + } + + return ca, caPrivKey, nil +} + +// GenerateCSR - generate certificate signing request +func GenerateCSR(namespace string, serviceName string) (*x509.Certificate, *rsa.PrivateKey, error) { + csr := &x509.Certificate{ + SerialNumber: big.NewInt(1234), + Subject: pkix.Name{ + Organization: []string{"kubearmor"}, + Country: []string{"US"}, + Province: []string{""}, + CommonName: "kubearmor-webhook", + }, + DNSNames: []string{ + serviceName + "." + namespace + ".svc", + serviceName + "." + namespace + ".svc.cluster.local", + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(3, 0, 0), + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature, + SubjectKeyId: []byte{1, 2, 3, 4, 5}, + BasicConstraintsValid: true, + } + certPrivKey, err := rsa.GenerateKey(rand.Reader, 4096) + if err != nil { + return &x509.Certificate{}, &rsa.PrivateKey{}, errors.New("cannot generate csr private key") + } + return csr, certPrivKey, nil +} + +// SignCSR - signs a certificate signing request essentially approving it using the given CA +func SignCSR(caCrt *x509.Certificate, caKey *rsa.PrivateKey, csrCrt *x509.Certificate, csrKey *rsa.PrivateKey) ([]byte, error) { + certBytes, err := x509.CreateCertificate(rand.Reader, csrCrt, caCrt, &csrKey.PublicKey, caKey) + if err != nil { + return []byte{}, errors.New("cannot sign the csr") + } + return certBytes, nil +} diff --git a/pkg/KubeArmorOperator/config/crd/bases/operator.kubearmor.com_kubearmorconfigs.yaml b/pkg/KubeArmorOperator/config/crd/bases/operator.kubearmor.com_kubearmorconfigs.yaml new file mode 100644 index 0000000000..0bcf52eef5 --- /dev/null +++ b/pkg/KubeArmorOperator/config/crd/bases/operator.kubearmor.com_kubearmorconfigs.yaml @@ -0,0 +1,147 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.1 + creationTimestamp: null + name: kubearmorconfigs.operator.kubearmor.com +spec: + group: operator.kubearmor.com + names: + kind: KubeArmorConfig + listKind: KubeArmorConfigList + plural: kubearmorconfigs + singular: kubearmorconfig + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.phase + name: Status + type: string + name: v1 + schema: + openAPIV3Schema: + description: KubeArmorConfig is the Schema for the KubeArmorConfigs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: KubeArmorConfigSpec defines the desired state of KubeArmorConfig + properties: + defaultCapabilitiesPosture: + enum: + - audit + - block + type: string + defaultFilePosture: + enum: + - audit + - block + type: string + defaultNetworkPosture: + enum: + - audit + - block + type: string + defaultVisibility: + type: string + kubeRbacProxyImage: + description: ImageSpec defines the image specifications + properties: + image: + type: string + imagePullPolicy: + default: Always + enum: + - Always + - IfNotPresent + - Never + type: string + type: object + kubearmorControllerImage: + description: ImageSpec defines the image specifications + properties: + image: + type: string + imagePullPolicy: + default: Always + enum: + - Always + - IfNotPresent + - Never + type: string + type: object + kubearmorImage: + description: ImageSpec defines the image specifications + properties: + image: + type: string + imagePullPolicy: + default: Always + enum: + - Always + - IfNotPresent + - Never + type: string + type: object + kubearmorInitImage: + description: ImageSpec defines the image specifications + properties: + image: + type: string + imagePullPolicy: + default: Always + enum: + - Always + - IfNotPresent + - Never + type: string + type: object + kubearmorRelayImage: + description: ImageSpec defines the image specifications + properties: + image: + type: string + imagePullPolicy: + default: Always + enum: + - Always + - IfNotPresent + - Never + type: string + type: object + type: object + status: + description: KubeArmorConfigStatus defines the observed state of KubeArmorConfig + properties: + message: + type: string + phase: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/pkg/KubeArmorOperator/config/crd/kustomization.yaml b/pkg/KubeArmorOperator/config/crd/kustomization.yaml new file mode 100644 index 0000000000..4e317fba49 --- /dev/null +++ b/pkg/KubeArmorOperator/config/crd/kustomization.yaml @@ -0,0 +1,21 @@ +# This kustomization.yaml is not intended to be run by itself, +# since it depends on service name and namespace that are out of this kustomize package. +# It should be run by config/default +resources: +- bases/operator.kubearmor.com_kubearmorconfigs.yaml +#+kubebuilder:scaffold:crdkustomizeresource + +patchesStrategicMerge: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +#- patches/webhook_in_kubearmorconfigs.yaml +#+kubebuilder:scaffold:crdkustomizewebhookpatch + +# [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. +# patches here are for enabling the CA injection for each CRD +#- patches/cainjection_in_kubearmorconfigs.yaml +#+kubebuilder:scaffold:crdkustomizecainjectionpatch + +# the following config is for teaching kustomize how to do kustomization for CRDs. +configurations: +- kustomizeconfig.yaml diff --git a/pkg/KubeArmorOperator/config/crd/kustomizeconfig.yaml b/pkg/KubeArmorOperator/config/crd/kustomizeconfig.yaml new file mode 100644 index 0000000000..ec5c150a9d --- /dev/null +++ b/pkg/KubeArmorOperator/config/crd/kustomizeconfig.yaml @@ -0,0 +1,19 @@ +# This file is for teaching kustomize how to substitute name and namespace reference in CRD +nameReference: +- kind: Service + version: v1 + fieldSpecs: + - kind: CustomResourceDefinition + version: v1 + group: apiextensions.k8s.io + path: spec/conversion/webhook/clientConfig/service/name + +namespace: +- kind: CustomResourceDefinition + version: v1 + group: apiextensions.k8s.io + path: spec/conversion/webhook/clientConfig/service/namespace + create: false + +varReference: +- path: metadata/annotations diff --git a/pkg/KubeArmorOperator/config/crd/patches/cainjection_in_kubearmorconfigs.yaml b/pkg/KubeArmorOperator/config/crd/patches/cainjection_in_kubearmorconfigs.yaml new file mode 100644 index 0000000000..0a17f8173a --- /dev/null +++ b/pkg/KubeArmorOperator/config/crd/patches/cainjection_in_kubearmorconfigs.yaml @@ -0,0 +1,7 @@ +# The following patch adds a directive for certmanager to inject CA into the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: CERTIFICATE_NAMESPACE/CERTIFICATE_NAME + name: kubearmorconfigs.operator.kubearmor.com diff --git a/pkg/KubeArmorOperator/config/crd/patches/webhook_in_kubearmorconfigs.yaml b/pkg/KubeArmorOperator/config/crd/patches/webhook_in_kubearmorconfigs.yaml new file mode 100644 index 0000000000..5a86cf5ac3 --- /dev/null +++ b/pkg/KubeArmorOperator/config/crd/patches/webhook_in_kubearmorconfigs.yaml @@ -0,0 +1,16 @@ +# The following patch enables a conversion webhook for the CRD +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: kubearmorconfigs.operator.kubearmor.com +spec: + conversion: + strategy: Webhook + webhook: + clientConfig: + service: + namespace: system + name: webhook-service + path: /convert + conversionReviewVersions: + - v1 diff --git a/pkg/KubeArmorOperator/config/default/kustomization.yaml b/pkg/KubeArmorOperator/config/default/kustomization.yaml new file mode 100644 index 0000000000..7da0c28ad3 --- /dev/null +++ b/pkg/KubeArmorOperator/config/default/kustomization.yaml @@ -0,0 +1,9 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +namespace: kube-system + +resources: +- ../crd +- ../rbac +- ../operator diff --git a/pkg/KubeArmorOperator/config/operator/deployment.yaml b/pkg/KubeArmorOperator/config/operator/deployment.yaml new file mode 100644 index 0000000000..92b8d33c94 --- /dev/null +++ b/pkg/KubeArmorOperator/config/operator/deployment.yaml @@ -0,0 +1,26 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kubearmor-operator +spec: + selector: + matchLabels: + kubearmor-app: kubearmor-operator + template: + metadata: + labels: + kubearmor-app: kubearmor-operator + spec: + serviceAccountName: kubearmor-operator + containers: + - name: operator + image: kubearmor/kubearmor-operator:latest + imagePullPolicy: IfNotPresent + command: + - "/operator" + - "kubearmor-operator" + env: + - name: KUBEARMOR_OPERATOR_NS + valueFrom: + fieldRef: + fieldPath: metadata.namespace \ No newline at end of file diff --git a/pkg/KubeArmorOperator/config/operator/kustomization.yaml b/pkg/KubeArmorOperator/config/operator/kustomization.yaml new file mode 100644 index 0000000000..1d3ac2382e --- /dev/null +++ b/pkg/KubeArmorOperator/config/operator/kustomization.yaml @@ -0,0 +1,5 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - "deployment.yaml" diff --git a/pkg/KubeArmorOperator/config/rbac/clusterrole.yaml b/pkg/KubeArmorOperator/config/rbac/clusterrole.yaml new file mode 100644 index 0000000000..2c99fc9e28 --- /dev/null +++ b/pkg/KubeArmorOperator/config/rbac/clusterrole.yaml @@ -0,0 +1,224 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kubearmor-operator-clusterrole +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - watch + - list + - patch +- apiGroups: + - "" + resources: + - pods + verbs: + - list +- apiGroups: + - "" + resources: + - secrets + - serviceaccounts + - services + - configmaps + verbs: + - get + - create + - delete + - update +- apiGroups: + - apps + resources: + - deployments + - daemonsets + verbs: + - list + - get + - create + - delete + - update +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + verbs: + - get + - create + - delete +- apiGroups: + - batch + verbs: + - create + resources: + - jobs +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + - clusterroles + - clusterrolebindings + verbs: + - create + - get +- apiGroups: + - operator.kubearmor.com + resources: + - kubearmorconfigs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - operator.kubearmor.com + resources: + - kubearmorconfigs/status + verbs: + - get + - patch + - update +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kubearmor-operator-manage-kubearmor-clusterrole +rules: +- apiGroups: + - "" + resources: + - pods + - nodes + - namespaces + - configmaps + verbs: + - get + - patch + - list + - watch + - update +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - patch + - list + - watch + - update +- apiGroups: + - apps + resources: + - replicasets + - daemonsets + - statefulsets + verbs: + - get + - list + - watch +- apiGroups: + - security.kubearmor.com + resources: + - kubearmorpolicies + - kubearmorhostpolicies + verbs: + - get + - list + - watch + - update + - delete +- nonResourceURLs: + - /apis + - /apis/* + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kubearmor-operator-manage-controller-clusterrole +rules: +- apiGroups: + - "" + resources: + - pods + - configmaps + verbs: + - create + - delete + - get + - patch + - list + - watch + - update +- apiGroups: + - security.kubearmor.com + resources: + - kubearmorpolicies + - kubearmorhostpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - security.kubearmor.com + resources: + - kubearmorpolicies/status + - kubearmorhostpolicies/status + verbs: + - get + - patch + - update +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +- nonResourceURLs: + - /metrics + verbs: + - get \ No newline at end of file diff --git a/pkg/KubeArmorOperator/config/rbac/clusterrolebinding.yaml b/pkg/KubeArmorOperator/config/rbac/clusterrolebinding.yaml new file mode 100644 index 0000000000..ceb46b4d26 --- /dev/null +++ b/pkg/KubeArmorOperator/config/rbac/clusterrolebinding.yaml @@ -0,0 +1,38 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kubearmor-operator-clusterrole-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kubearmor-operator-clusterrole +subjects: +- kind: ServiceAccount + name: kubearmor-operator + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kubearmor-operator-manage-kubearmor-clusterrole-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kubearmor-operator-manage-kubearmor-clusterrole +subjects: +- kind: ServiceAccount + name: kubearmor-operator + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kubearmor-operator-manage-controller-clusterrole-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kubearmor-operator-manage-controller-clusterrole +subjects: +- kind: ServiceAccount + name: kubearmor-operator + namespace: kube-system \ No newline at end of file diff --git a/pkg/KubeArmorOperator/config/rbac/kustomization.yaml b/pkg/KubeArmorOperator/config/rbac/kustomization.yaml new file mode 100644 index 0000000000..30319d679b --- /dev/null +++ b/pkg/KubeArmorOperator/config/rbac/kustomization.yaml @@ -0,0 +1,7 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization + +resources: + - "clusterrole.yaml" + - "clusterrolebinding.yaml" + - "service-account.yaml" \ No newline at end of file diff --git a/pkg/KubeArmorOperator/config/rbac/service-account.yaml b/pkg/KubeArmorOperator/config/rbac/service-account.yaml new file mode 100644 index 0000000000..c6cdfdabc2 --- /dev/null +++ b/pkg/KubeArmorOperator/config/rbac/service-account.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kubearmor-operator diff --git a/pkg/KubeArmorOperator/config/samples/kubearmor-test.yaml b/pkg/KubeArmorOperator/config/samples/kubearmor-test.yaml new file mode 100644 index 0000000000..2a9b12a7c7 --- /dev/null +++ b/pkg/KubeArmorOperator/config/samples/kubearmor-test.yaml @@ -0,0 +1,28 @@ +apiVersion: operator.kubearmor.com/v1 +kind: KubeArmorConfig +metadata: + labels: + app.kubernetes.io/name: kubearmorconfig + app.kubernetes.io/instance: kubearmorconfig-sample + app.kubernetes.io/part-of: kubearmoroperator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: kubearmoroperator + name: kubearmorconfig-test + namespace: kube-system +spec: + defaultCapabilitiesPosture: block + defaultFilePosture: block + defaultNetworkPosture: block + defaultVisibility: process,file,network + kubearmorImage: + image: kubearmor/kubearmor:latest + imagePullPolicy: Never + kubearmorInitImage: + image: kubearmor/kubearmor-init:latest + imagePullPolicy: Never + kubearmorRelayImage: + image: kubearmor/kubearmor-relay-server:latest + imagePullPolicy: Always + kubearmorControllerImage: + image: kubearmor/kubearmor-controller:latest + imagePullPolicy: Always diff --git a/pkg/KubeArmorOperator/config/samples/kustomization.yaml b/pkg/KubeArmorOperator/config/samples/kustomization.yaml new file mode 100644 index 0000000000..84de33d207 --- /dev/null +++ b/pkg/KubeArmorOperator/config/samples/kustomization.yaml @@ -0,0 +1,4 @@ +## Append samples of your project ## +resources: +- operator_v1_kubearmorconfig.yaml +#+kubebuilder:scaffold:manifestskustomizesamples diff --git a/pkg/KubeArmorOperator/config/samples/operator_v1_kubearmorconfig.yaml b/pkg/KubeArmorOperator/config/samples/operator_v1_kubearmorconfig.yaml new file mode 100644 index 0000000000..7a1b9f6877 --- /dev/null +++ b/pkg/KubeArmorOperator/config/samples/operator_v1_kubearmorconfig.yaml @@ -0,0 +1,12 @@ +apiVersion: operator.kubearmor.com/v1 +kind: KubeArmorConfig +metadata: + labels: + app.kubernetes.io/name: kubearmorconfig + app.kubernetes.io/instance: kubearmorconfig-sample + app.kubernetes.io/part-of: kubearmoroperator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: kubearmoroperator + name: kubearmorconfig-sample +spec: + # TODO(user): Add fields here diff --git a/pkg/KubeArmorOperator/config/samples/sample-config.yml b/pkg/KubeArmorOperator/config/samples/sample-config.yml new file mode 100644 index 0000000000..a9cd4866a1 --- /dev/null +++ b/pkg/KubeArmorOperator/config/samples/sample-config.yml @@ -0,0 +1,28 @@ +apiVersion: operator.kubearmor.com/v1 +kind: KubeArmorConfig +metadata: + labels: + app.kubernetes.io/name: kubearmorconfig + app.kubernetes.io/instance: kubearmorconfig-sample + app.kubernetes.io/part-of: kubearmoroperator + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: kubearmoroperator + name: kubearmorconfig-default + namespace: kube-system +spec: + defaultCapabilitiesPosture: audit + defaultFilePosture: audit + defaultNetworkPosture: audit + defaultVisibility: process,file,network + kubearmorImage: + image: kubearmor/kubearmor:stable + imagePullPolicy: Always + kubearmorInitImage: + image: kubearmor/kubearmor-init:stable + imagePullPolicy: Always + kubearmorRelayImage: + image: kubearmor/kubearmor-relay-server + imagePullPolicy: Always + kubearmorControllerImage: + image: kubearmor/kubearmor-controller + imagePullPolicy: Always diff --git a/pkg/KubeArmorOperator/deployments/helm/.helmignore b/pkg/KubeArmorOperator/deployments/helm/.helmignore new file mode 100644 index 0000000000..0e8a0eb36f --- /dev/null +++ b/pkg/KubeArmorOperator/deployments/helm/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/pkg/KubeArmorOperator/deployments/helm/Chart.yaml b/pkg/KubeArmorOperator/deployments/helm/Chart.yaml new file mode 100644 index 0000000000..5c9b02fd72 --- /dev/null +++ b/pkg/KubeArmorOperator/deployments/helm/Chart.yaml @@ -0,0 +1,21 @@ +apiVersion: v2 +name: kubearmor-operator +description: A Helm chart for kubearmor operator +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "0.1.0" diff --git a/pkg/KubeArmorOperator/deployments/helm/crds/operator.kubearmor.com_kubearmorconfigs.yaml b/pkg/KubeArmorOperator/deployments/helm/crds/operator.kubearmor.com_kubearmorconfigs.yaml new file mode 100644 index 0000000000..0bcf52eef5 --- /dev/null +++ b/pkg/KubeArmorOperator/deployments/helm/crds/operator.kubearmor.com_kubearmorconfigs.yaml @@ -0,0 +1,147 @@ + +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.1 + creationTimestamp: null + name: kubearmorconfigs.operator.kubearmor.com +spec: + group: operator.kubearmor.com + names: + kind: KubeArmorConfig + listKind: KubeArmorConfigList + plural: kubearmorconfigs + singular: kubearmorconfig + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.phase + name: Status + type: string + name: v1 + schema: + openAPIV3Schema: + description: KubeArmorConfig is the Schema for the KubeArmorConfigs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: KubeArmorConfigSpec defines the desired state of KubeArmorConfig + properties: + defaultCapabilitiesPosture: + enum: + - audit + - block + type: string + defaultFilePosture: + enum: + - audit + - block + type: string + defaultNetworkPosture: + enum: + - audit + - block + type: string + defaultVisibility: + type: string + kubeRbacProxyImage: + description: ImageSpec defines the image specifications + properties: + image: + type: string + imagePullPolicy: + default: Always + enum: + - Always + - IfNotPresent + - Never + type: string + type: object + kubearmorControllerImage: + description: ImageSpec defines the image specifications + properties: + image: + type: string + imagePullPolicy: + default: Always + enum: + - Always + - IfNotPresent + - Never + type: string + type: object + kubearmorImage: + description: ImageSpec defines the image specifications + properties: + image: + type: string + imagePullPolicy: + default: Always + enum: + - Always + - IfNotPresent + - Never + type: string + type: object + kubearmorInitImage: + description: ImageSpec defines the image specifications + properties: + image: + type: string + imagePullPolicy: + default: Always + enum: + - Always + - IfNotPresent + - Never + type: string + type: object + kubearmorRelayImage: + description: ImageSpec defines the image specifications + properties: + image: + type: string + imagePullPolicy: + default: Always + enum: + - Always + - IfNotPresent + - Never + type: string + type: object + type: object + status: + description: KubeArmorConfigStatus defines the observed state of KubeArmorConfig + properties: + message: + type: string + phase: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/pkg/KubeArmorOperator/deployments/helm/templates/clusterrole-binding-rbac.yaml b/pkg/KubeArmorOperator/deployments/helm/templates/clusterrole-binding-rbac.yaml new file mode 100644 index 0000000000..7c246ca9ca --- /dev/null +++ b/pkg/KubeArmorOperator/deployments/helm/templates/clusterrole-binding-rbac.yaml @@ -0,0 +1,38 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ .Values.kubearmorOperator.name }}-clusterrole-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ .Values.kubearmorOperator.name }}-clusterrole +subjects: +- kind: ServiceAccount + name: {{ .Values.kubearmorOperator.name }} + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ .Values.kubearmorOperator.name }}-manage-kubearmor-clusterrole-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ .Values.kubearmorOperator.name }}-manage-kubearmor-clusterrole +subjects: +- kind: ServiceAccount + name: {{ .Values.kubearmorOperator.name }} + namespace: {{ .Release.Namespace }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ .Values.kubearmorOperator.name }}-manage-controller-clusterrole-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ .Values.kubearmorOperator.name }}-manage-controller-clusterrole +subjects: +- kind: ServiceAccount + name: {{ .Values.kubearmorOperator.name }} + namespace: {{ .Release.Namespace }} \ No newline at end of file diff --git a/pkg/KubeArmorOperator/deployments/helm/templates/clusterrole-rbac.yaml b/pkg/KubeArmorOperator/deployments/helm/templates/clusterrole-rbac.yaml new file mode 100644 index 0000000000..6e89e2be8b --- /dev/null +++ b/pkg/KubeArmorOperator/deployments/helm/templates/clusterrole-rbac.yaml @@ -0,0 +1,224 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ .Values.kubearmorOperator.name }}-clusterrole +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - watch + - list + - patch +- apiGroups: + - "" + resources: + - pods + verbs: + - list +- apiGroups: + - "" + resources: + - secrets + - serviceaccounts + - services + - configmaps + verbs: + - get + - create + - delete + - update +- apiGroups: + - apps + resources: + - deployments + - daemonsets + verbs: + - list + - get + - create + - delete + - update +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + verbs: + - get + - create + - delete +- apiGroups: + - batch + verbs: + - create + resources: + - jobs +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + - clusterroles + - clusterrolebindings + verbs: + - create + - get +- apiGroups: + - operator.kubearmor.com + resources: + - kubearmorconfigs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - operator.kubearmor.com + resources: + - kubearmorconfigs/status + verbs: + - get + - patch + - update +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ .Values.kubearmorOperator.name }}-manage-kubearmor-clusterrole +rules: +- apiGroups: + - "" + resources: + - pods + - nodes + - namespaces + - configmaps + verbs: + - get + - patch + - list + - watch + - update +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - patch + - list + - watch + - update +- apiGroups: + - apps + resources: + - replicasets + - daemonsets + - statefulsets + verbs: + - get + - list + - watch +- apiGroups: + - security.kubearmor.com + resources: + - kubearmorpolicies + - kubearmorhostpolicies + verbs: + - get + - list + - watch + - update + - delete +- nonResourceURLs: + - /apis + - /apis/* + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ .Values.kubearmorOperator.name }}-manage-controller-clusterrole +rules: +- apiGroups: + - "" + resources: + - pods + - configmaps + verbs: + - create + - delete + - get + - patch + - list + - watch + - update +- apiGroups: + - security.kubearmor.com + resources: + - kubearmorpolicies + - kubearmorhostpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - security.kubearmor.com + resources: + - kubearmorpolicies/status + - kubearmorhostpolicies/status + verbs: + - get + - patch + - update +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +- nonResourceURLs: + - /metrics + verbs: + - get \ No newline at end of file diff --git a/pkg/KubeArmorOperator/deployments/helm/templates/deployment.yaml b/pkg/KubeArmorOperator/deployments/helm/templates/deployment.yaml new file mode 100644 index 0000000000..632b50bf98 --- /dev/null +++ b/pkg/KubeArmorOperator/deployments/helm/templates/deployment.yaml @@ -0,0 +1,31 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.kubearmorOperator.name }} + namespace: {{ .Release.Namespace }} + labels: + kubearmor-app: {{ .Values.kubearmorOperator.name }} +spec: + selector: + matchLabels: + kubearmor-app: {{ .Values.kubearmorOperator.name }} + template: + metadata: + labels: + kubearmor-app: {{ .Values.kubearmorOperator.name }} + spec: + containers: + - command: + - /operator + - kubearmor-operator + env: + - name: KUBEARMOR_OPERATOR_NS + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: KUBERNETES_CLUSTER_DOMAIN + value: {{ quote .Values.kubernetesClusterDomain }} + image: {{ printf "%s:%s" .Values.kubearmorOperator.image.repository .Values.kubearmorOperator.image.tag}} + imagePullPolicy: {{ .Values.kubearmorOperator.imagePullPolicy }} + name: {{ .Values.kubearmorOperator.name }} + serviceAccountName: {{ .Values.kubearmorOperator.name }} \ No newline at end of file diff --git a/pkg/KubeArmorOperator/deployments/helm/templates/serviceaccount.yaml b/pkg/KubeArmorOperator/deployments/helm/templates/serviceaccount.yaml new file mode 100644 index 0000000000..63ad150372 --- /dev/null +++ b/pkg/KubeArmorOperator/deployments/helm/templates/serviceaccount.yaml @@ -0,0 +1,5 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ .Values.kubearmorOperator.name }} + namespace: {{ .Release.Namespace }} \ No newline at end of file diff --git a/pkg/KubeArmorOperator/deployments/helm/values.yaml b/pkg/KubeArmorOperator/deployments/helm/values.yaml new file mode 100644 index 0000000000..eee3362782 --- /dev/null +++ b/pkg/KubeArmorOperator/deployments/helm/values.yaml @@ -0,0 +1,9 @@ +kubearmorOperator: + name: kubearmor-operator + image: + repository: kubearmor/kubearmor-operator + tag: latest + imagePullPolicy: IfNotPresent + serviceAccount: + annotations: {} +kubernetesClusterDomain: cluster.local diff --git a/pkg/KubeArmorOperator/deployments/operator.yaml b/pkg/KubeArmorOperator/deployments/operator.yaml new file mode 100644 index 0000000000..79a7eea0b2 --- /dev/null +++ b/pkg/KubeArmorOperator/deployments/operator.yaml @@ -0,0 +1,443 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.4.1 + creationTimestamp: null + name: kubearmorconfigs.operator.kubearmor.com +spec: + group: operator.kubearmor.com + names: + kind: KubeArmorConfig + listKind: KubeArmorConfigList + plural: kubearmorconfigs + singular: kubearmorconfig + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.phase + name: Status + type: string + name: v1 + schema: + openAPIV3Schema: + description: KubeArmorConfig is the Schema for the KubeArmorConfigs API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: KubeArmorConfigSpec defines the desired state of KubeArmorConfig + properties: + defaultCapabilitiesPosture: + enum: + - audit + - block + type: string + defaultFilePosture: + enum: + - audit + - block + type: string + defaultNetworkPosture: + enum: + - audit + - block + type: string + defaultVisibility: + type: string + kubeRbacProxyImage: + description: ImageSpec defines the image specifications + properties: + image: + type: string + imagePullPolicy: + default: Always + enum: + - Always + - IfNotPresent + - Never + type: string + type: object + kubearmorControllerImage: + description: ImageSpec defines the image specifications + properties: + image: + type: string + imagePullPolicy: + default: Always + enum: + - Always + - IfNotPresent + - Never + type: string + type: object + kubearmorImage: + description: ImageSpec defines the image specifications + properties: + image: + type: string + imagePullPolicy: + default: Always + enum: + - Always + - IfNotPresent + - Never + type: string + type: object + kubearmorInitImage: + description: ImageSpec defines the image specifications + properties: + image: + type: string + imagePullPolicy: + default: Always + enum: + - Always + - IfNotPresent + - Never + type: string + type: object + kubearmorRelayImage: + description: ImageSpec defines the image specifications + properties: + image: + type: string + imagePullPolicy: + default: Always + enum: + - Always + - IfNotPresent + - Never + type: string + type: object + type: object + status: + description: KubeArmorConfigStatus defines the observed state of KubeArmorConfig + properties: + message: + type: string + phase: + description: 'INSERT ADDITIONAL STATUS FIELD - define observed state + of cluster Important: Run "make" to regenerate code after modifying + this file' + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kubearmor-operator + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kubearmor-operator-clusterrole +rules: +- apiGroups: + - "" + resources: + - nodes + verbs: + - get + - watch + - list + - patch +- apiGroups: + - "" + resources: + - pods + verbs: + - list +- apiGroups: + - "" + resources: + - secrets + - serviceaccounts + - services + - configmaps + verbs: + - get + - create + - delete + - update +- apiGroups: + - apps + resources: + - deployments + - daemonsets + verbs: + - list + - get + - create + - delete + - update +- apiGroups: + - admissionregistration.k8s.io + resources: + - mutatingwebhookconfigurations + verbs: + - get + - create + - delete +- apiGroups: + - batch + resources: + - jobs + verbs: + - create +- apiGroups: + - rbac.authorization.k8s.io + resources: + - roles + - rolebindings + - clusterroles + - clusterrolebindings + verbs: + - create + - get +- apiGroups: + - operator.kubearmor.com + resources: + - kubearmorconfigs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - operator.kubearmor.com + resources: + - kubearmorconfigs/status + verbs: + - get + - patch + - update +- apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kubearmor-operator-manage-controller-clusterrole +rules: +- apiGroups: + - "" + resources: + - pods + - configmaps + verbs: + - create + - delete + - get + - patch + - list + - watch + - update +- apiGroups: + - security.kubearmor.com + resources: + - kubearmorpolicies + - kubearmorhostpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - security.kubearmor.com + resources: + - kubearmorpolicies/status + - kubearmorhostpolicies/status + verbs: + - get + - patch + - update +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +- nonResourceURLs: + - /metrics + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: kubearmor-operator-manage-kubearmor-clusterrole +rules: +- apiGroups: + - "" + resources: + - pods + - nodes + - namespaces + - configmaps + verbs: + - get + - patch + - list + - watch + - update +- apiGroups: + - apps + resources: + - deployments + verbs: + - get + - patch + - list + - watch + - update +- apiGroups: + - apps + resources: + - replicasets + - daemonsets + - statefulsets + verbs: + - get + - list + - watch +- apiGroups: + - security.kubearmor.com + resources: + - kubearmorpolicies + - kubearmorhostpolicies + verbs: + - get + - list + - watch + - update + - delete +- nonResourceURLs: + - /apis + - /apis/* + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kubearmor-operator-clusterrole-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kubearmor-operator-clusterrole +subjects: +- kind: ServiceAccount + name: kubearmor-operator + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kubearmor-operator-manage-controller-clusterrole-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kubearmor-operator-manage-controller-clusterrole +subjects: +- kind: ServiceAccount + name: kubearmor-operator + namespace: kube-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kubearmor-operator-manage-kubearmor-clusterrole-binding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: kubearmor-operator-manage-kubearmor-clusterrole +subjects: +- kind: ServiceAccount + name: kubearmor-operator + namespace: kube-system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kubearmor-operator + namespace: kube-system +spec: + selector: + matchLabels: + kubearmor-app: kubearmor-operator + template: + metadata: + labels: + kubearmor-app: kubearmor-operator + spec: + containers: + - command: + - /operator + - kubearmor-operator + env: + - name: KUBEARMOR_OPERATOR_NS + valueFrom: + fieldRef: + fieldPath: metadata.namespace + image: kubearmor/kubearmor-operator:latest + imagePullPolicy: IfNotPresent + name: operator + serviceAccountName: kubearmor-operator diff --git a/pkg/KubeArmorOperator/enforcer/enforcer.go b/pkg/KubeArmorOperator/enforcer/enforcer.go new file mode 100644 index 0000000000..ed91fea6b8 --- /dev/null +++ b/pkg/KubeArmorOperator/enforcer/enforcer.go @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Authors of KubeArmor + +package enforcer + +import ( + "os" + "path/filepath" + "strings" + + probe "github.com/kubearmor/KubeArmor/KubeArmor/utils/bpflsmprobe" + "go.uber.org/zap" + "k8s.io/kubectl/pkg/util/slice" +) + +// GetAvailableLsms Functio +func GetAvailableLsms() []string { + return []string{"bpf", "selinux", "apparmor"} +} + +// DetectEnforcer: detect the enforcer on the node +func DetectEnforcer(lsmOrder []string, PathPrefix string, log zap.SugaredLogger) string { + supportedLsms := []string{} + lsm := []byte{} + lsmPath := PathPrefix + "/sys/kernel/security/lsm" + + if _, err := os.Stat(filepath.Clean(lsmPath)); err == nil { + lsm, err = os.ReadFile(lsmPath) + if err != nil { + log.Info("Failed to read /sys/kernel/security/lsm " + err.Error()) + goto probeLSM + } + } + + supportedLsms = strings.Split(string(lsm), ",") + +probeLSM: + if !slice.ContainsString(supportedLsms, "bpf", nil) { + err := probe.CheckBPFLSMSupport() + if err == nil { + supportedLsms = append(supportedLsms, "bpf") + } else { + log.Warnf("BPF LSM not supported %s", err.Error()) + } + } + + // Check if the AppArmor module is enabled on the system. + // Refer to Kubernetes documentation for more details: + // https://kubernetes.io/docs/tutorials/security/apparmor/#before-you-begin + if !slice.ContainsString(supportedLsms, "apparmor", nil) { + apparmorModule := PathPrefix + "/sys/module/apparmor/parameters/enabled" + if _, err := os.Stat(filepath.Clean(apparmorModule)); err == nil { + data, err := os.ReadFile(apparmorModule) + if err == nil { + status := strings.TrimSpace(string(data)) + if status == "Y" { + supportedLsms = append(supportedLsms, "apparmor") + } else { + log.Warn("Apparmor not supported") + } + } else { + log.Info("Failed to read /sys/module/apparmor/parameters/enabled " + err.Error()) + } + } + } + + log.Infof("/sys/kernel/security/lsm : %s", string(lsm)) + log.Infof("Supported LSMs %s", strings.Join(supportedLsms, ",")) + + return selectLsm(lsmOrder, GetAvailableLsms(), supportedLsms) +} + +// selectLsm Function +func selectLsm(lsmOrder, availablelsms, supportedlsm []string) string { + var lsm string + +lsmselection: + //check lsm preference order + if len(lsmOrder) != 0 { + lsm = lsmOrder[0] + lsmOrder = lsmOrder[1:] + if slice.ContainsString(supportedlsm, lsm, nil) && slice.ContainsString(availablelsms, lsm, nil) { + return lsm + } + goto lsmselection + } + + // fallback to available lsms order + if len(availablelsms) != 0 { + lsm = availablelsms[0] + availablelsms = availablelsms[1:] + if slice.ContainsString(supportedlsm, lsm, nil) { + return lsm + } + goto lsmselection + } + + return "NA" +} diff --git a/pkg/KubeArmorOperator/go.mod b/pkg/KubeArmorOperator/go.mod new file mode 100644 index 0000000000..43b694e661 --- /dev/null +++ b/pkg/KubeArmorOperator/go.mod @@ -0,0 +1,76 @@ +module github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator + +go 1.20 + +require ( + github.com/kubearmor/KubeArmor/KubeArmor v0.0.0-20230801181826-f1b41d01e8c1 + github.com/kubearmor/KubeArmor/deployments v0.0.0-20230711122007-7ab3a56cfd76 + github.com/kubearmor/KubeArmor/pkg/KubeArmorController v0.0.0-20230711122007-7ab3a56cfd76 + github.com/spf13/cobra v1.7.0 + go.uber.org/zap v1.24.0 + golang.org/x/mod v0.10.0 + k8s.io/api v0.27.3 + k8s.io/apiextensions-apiserver v0.27.3 + k8s.io/apimachinery v0.27.3 + k8s.io/client-go v0.27.3 + k8s.io/kubectl v0.27.2 + sigs.k8s.io/controller-runtime v0.15.0 +) + +require ( + github.com/cilium/ebpf v0.11.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.10.2 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gnostic v0.6.9 // indirect + github.com/google/go-cmp v0.5.9 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/imdario/mergo v0.3.16 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // 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/pelletier/go-toml/v2 v2.0.8 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/spf13/afero v1.9.5 // indirect + github.com/spf13/cast v1.5.1 // indirect + github.com/spf13/jwalterweatherman v1.1.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.16.0 // indirect + github.com/subosito/gotenv v1.4.2 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect + golang.org/x/net v0.11.0 // indirect + golang.org/x/oauth2 v0.8.0 // indirect + golang.org/x/sys v0.10.0 // indirect + golang.org/x/term v0.9.0 // indirect + golang.org/x/text v0.10.0 // indirect + golang.org/x/time v0.3.0 // indirect + google.golang.org/appengine v1.6.7 // indirect + google.golang.org/protobuf v1.30.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/kube-openapi v0.0.0-20230525220651-2546d827e515 // indirect + k8s.io/utils v0.0.0-20230505201702-9f6742963106 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) diff --git a/pkg/KubeArmorOperator/go.sum b/pkg/KubeArmorOperator/go.sum new file mode 100644 index 0000000000..d39e56768b --- /dev/null +++ b/pkg/KubeArmorOperator/go.sum @@ -0,0 +1,639 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.11.0 h1:V8gS/bTCCjX9uUnkUFUpPsksM8n1lXBAvHcpiFk1X2Y= +github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUgWsVs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE= +github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= +github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= +github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +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/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +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/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +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/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kubearmor/KubeArmor/KubeArmor v0.0.0-20230801181826-f1b41d01e8c1 h1:dM1LUXKiEK8xlXg7drlT331vHjB2ZIiqer4fRCq/ZkE= +github.com/kubearmor/KubeArmor/KubeArmor v0.0.0-20230801181826-f1b41d01e8c1/go.mod h1:6Ha3nBwlgPnTiacKade2jxkkBpOKPmBCizrsJcxn+fw= +github.com/kubearmor/KubeArmor/deployments v0.0.0-20230711122007-7ab3a56cfd76 h1:ttcJscoFJVdEKdH/7XjAOB2Rt9AZtyOR4lVSSV8nRIE= +github.com/kubearmor/KubeArmor/deployments v0.0.0-20230711122007-7ab3a56cfd76/go.mod h1:9c5VpVVkkto1fPLyCrnZAThQ9lO04mpYNJnfN36UqL0= +github.com/kubearmor/KubeArmor/pkg/KubeArmorController v0.0.0-20230711122007-7ab3a56cfd76 h1:zWcxvEcrD2A5y94g10woJYb047j4pGgIPuXxwrw0I2Y= +github.com/kubearmor/KubeArmor/pkg/KubeArmorController v0.0.0-20230711122007-7ab3a56cfd76/go.mod h1:LtrnsoV9isTrXx0ClJgKxSJ/gbYmDj9m3pXIjG1yxBg= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +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.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= +github.com/onsi/gomega v1.27.8 h1:gegWiwZjBsf2DgiSbf5hpokZ98JVDMcWkUiigk6/KXc= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= +github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= +github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +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/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= +github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +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.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +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/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= +golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +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/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk= +golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU= +golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= +golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28= +golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= +golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo= +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= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y= +k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg= +k8s.io/apiextensions-apiserver v0.27.3 h1:xAwC1iYabi+TDfpRhxh4Eapl14Hs2OftM2DN5MpgKX4= +k8s.io/apiextensions-apiserver v0.27.3/go.mod h1:BH3wJ5NsB9XE1w+R6SSVpKmYNyIiyIz9xAmBl8Mb+84= +k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM= +k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= +k8s.io/client-go v0.27.3 h1:7dnEGHZEJld3lYwxvLl7WoehK6lAq7GvgjxpA3nv1E8= +k8s.io/client-go v0.27.3/go.mod h1:2MBEKuTo6V1lbKy3z1euEGnhPfGZLKTS9tiJ2xodM48= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230525220651-2546d827e515 h1:OmK1d0WrkD3IPfkskvroRykOulHVHf0s0ZIFRjyt+UI= +k8s.io/kube-openapi v0.0.0-20230525220651-2546d827e515/go.mod h1:kzo02I3kQ4BTtEfVLaPbjvCkX97YqGve33wzlb3fofQ= +k8s.io/kubectl v0.27.2 h1:sSBM2j94MHBFRWfHIWtEXWCicViQzZsb177rNsKBhZg= +k8s.io/kubectl v0.27.2/go.mod h1:GCOODtxPcrjh+EC611MqREkU8RjYBh10ldQCQ6zpFKw= +k8s.io/utils v0.0.0-20230505201702-9f6742963106 h1:EObNQ3TW2D+WptiYXlApGNLVy0zm/JIBVY9i+M4wpAU= +k8s.io/utils v0.0.0-20230505201702-9f6742963106/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU= +sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk= +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.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/pkg/KubeArmorOperator/hack/boilerplate.go.txt b/pkg/KubeArmorOperator/hack/boilerplate.go.txt new file mode 100644 index 0000000000..3434fa39ee --- /dev/null +++ b/pkg/KubeArmorOperator/hack/boilerplate.go.txt @@ -0,0 +1,2 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Authors of KubeArmor diff --git a/pkg/KubeArmorOperator/internal/controller/cluster.go b/pkg/KubeArmorOperator/internal/controller/cluster.go new file mode 100644 index 0000000000..65176f1796 --- /dev/null +++ b/pkg/KubeArmorOperator/internal/controller/cluster.go @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Authors of KubeArmor + +package controller + +import ( + "context" + "strings" + "sync" + "time" + + deployments "github.com/kubearmor/KubeArmor/deployments/get" + opv1 "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/api/operator.kubearmor.com/v1" + opv1client "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/clientset/versioned" + opv1Informer "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/informers/externalversions" + "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/common" + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/informers" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/cache" +) + +var informer informers.SharedInformerFactory +var deployment_uuid types.UID +var deployment_name string = "kubearmor-operator" +var PathPrefix string + +type ClusterWatcher struct { + Nodes []Node + NodesLock *sync.Mutex + Log *zap.SugaredLogger + Client *kubernetes.Clientset + ExtClient *apiextensionsclientset.Clientset + Opv1Client *opv1client.Clientset + Daemonsets map[string]int + DaemonsetsLock *sync.Mutex +} +type Node struct { + Name string + Enforcer string + Runtime string + RuntimeSocket string + RuntimeStorage string + Arch string +} + +func NewClusterWatcher(client *kubernetes.Clientset, log *zap.SugaredLogger, extClient *apiextensionsclientset.Clientset, opv1Client *opv1client.Clientset, pathPrefix, deploy_name string) *ClusterWatcher { + if informer == nil { + informer = informers.NewSharedInformerFactory(client, 0) + } + if deployment_uuid == "" { + deploy, err := client.AppsV1().Deployments(common.Namespace).Get(context.Background(), deployment_name, v1.GetOptions{}) + if err != nil { + log.Warnf("Cannot get deployment %s, error=%s", deployment_name, err.Error()) + } else { + deployment_uuid = deploy.GetUID() + common.OperatorImage = deploy.Spec.Template.Spec.Containers[0].Image + } + } + PathPrefix = pathPrefix + deployment_name = deploy_name + return &ClusterWatcher{ + Nodes: []Node{}, + Daemonsets: make(map[string]int), + Log: log, + NodesLock: &sync.Mutex{}, + DaemonsetsLock: &sync.Mutex{}, + Client: client, + ExtClient: extClient, + Opv1Client: opv1Client, + } +} + +func (clusterWatcher *ClusterWatcher) WatchNodes() { + log := clusterWatcher.Log + nodeInformer := informer.Core().V1().Nodes().Informer() + nodeInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + if node, ok := obj.(*corev1.Node); ok { + runtime := node.Status.NodeInfo.ContainerRuntimeVersion + runtime = strings.Split(runtime, ":")[0] + if val, ok := node.Labels[common.OsLabel]; ok && val == "linux" { + log.Infof("Installing snitch on node %s", node.Name) + _, err := clusterWatcher.Client.BatchV1().Jobs(common.Namespace).Create(context.Background(), deploySnitch(node.Name, runtime), v1.CreateOptions{}) + if err != nil { + log.Errorf("Cannot run snitch on node %s, error=%s", node.Name, err.Error()) + return + } + log.Infof("Snitch was installed on node %s", node.Name) + } + } + }, + UpdateFunc: func(oldObj, newObj interface{}) { + if node, ok := newObj.(*corev1.Node); ok { + oldRand := "" + if old, ok := oldObj.(*corev1.Node); ok { + oldRand = old.Labels[common.RandLabel] + } + if val, ok := node.Labels[common.OsLabel]; ok && val == "linux" && oldRand != node.Labels[common.RandLabel] { + newNode := Node{} + newNode.Name = node.Name + if val, ok := node.Labels[common.EnforcerLabel]; ok { + newNode.Enforcer = val + } + if val, ok := node.Labels[common.ArchLabel]; ok { + newNode.Arch = val + } + if val, ok := node.Labels[common.RuntimeLabel]; ok { + newNode.Runtime = val + } + if val, ok := node.Labels[common.SocketLabel]; ok { + newNode.RuntimeSocket = val + } + if val, ok := node.Labels[common.RuntimeStorageLabel]; ok { + newNode.RuntimeStorage = val + } + + clusterWatcher.NodesLock.Lock() + nbNodes := len(clusterWatcher.Nodes) + i := 0 + nodeModified := false + for i < nbNodes && newNode.Name != clusterWatcher.Nodes[i].Name { + i++ + } + if i == len(clusterWatcher.Nodes) { + clusterWatcher.Nodes = append(clusterWatcher.Nodes, newNode) + clusterWatcher.Log.Infof("Node %s has been added", newNode.Name) + } else { + if clusterWatcher.Nodes[i].Arch != newNode.Arch || + clusterWatcher.Nodes[i].Enforcer != newNode.Enforcer || + clusterWatcher.Nodes[i].Name != newNode.Name || + clusterWatcher.Nodes[i].Runtime != newNode.Runtime || + clusterWatcher.Nodes[i].RuntimeSocket != newNode.RuntimeSocket || + clusterWatcher.Nodes[i].RuntimeStorage != newNode.RuntimeStorage { + clusterWatcher.Nodes[i] = newNode + nodeModified = true + clusterWatcher.Log.Infof("Node %s was updated", node.Name) + } + } + clusterWatcher.NodesLock.Unlock() + if nodeModified { + clusterWatcher.UpdateDaemonsets(common.DeletAction, newNode.Enforcer, newNode.Runtime, newNode.RuntimeSocket, newNode.RuntimeStorage, node.Status.NodeInfo.KernelVersion) + } + clusterWatcher.UpdateDaemonsets(common.AddAction, newNode.Enforcer, newNode.Runtime, newNode.RuntimeSocket, newNode.RuntimeStorage, node.Status.NodeInfo.KernelVersion) + } + } else { + log.Errorf("Cannot convert object to node struct") + log.Error(newObj) + } + }, + DeleteFunc: func(obj interface{}) { + if node, ok := obj.(*corev1.Node); ok { + deletedNode := Node{} + clusterWatcher.NodesLock.Lock() + for i, n := range clusterWatcher.Nodes { + if n.Name == node.Name { + clusterWatcher.Nodes = append(clusterWatcher.Nodes[:i], clusterWatcher.Nodes[i+1:]...) + deletedNode = n + break + } + } + clusterWatcher.NodesLock.Unlock() + clusterWatcher.UpdateDaemonsets(common.DeletAction, deletedNode.Enforcer, deletedNode.Runtime, deletedNode.RuntimeSocket, deletedNode.RuntimeStorage, node.Status.NodeInfo.KernelVersion) + } + }, + }) + + nodeInformer.Run(wait.NeverStop) +} + +func (clusterWatcher *ClusterWatcher) UpdateDaemonsets(action, enforcer, runtime, socket, runtimeStorage, kernelVersion string) { + clusterWatcher.Log.Info("updating daemonset") + daemonsetName := strings.Join([]string{ + "kubearmor", + strings.ReplaceAll(enforcer, ".", "-"), + runtime, + common.ShortSHA(socket), + }, "-") + newDaemonSet := false + deleteDaemonSet := false + clusterWatcher.DaemonsetsLock.Lock() + if action == common.AddAction { + clusterWatcher.Daemonsets[daemonsetName]++ + _, err := clusterWatcher.Client.AppsV1().DaemonSets(common.Namespace).Get(context.Background(), daemonsetName, v1.GetOptions{}) + if err != nil { + newDaemonSet = true + } + } else if action == common.DeletAction { + if val, ok := clusterWatcher.Daemonsets[daemonsetName]; ok { + if val < 2 { + clusterWatcher.Daemonsets[daemonsetName] = 0 + deleteDaemonSet = true + } else { + clusterWatcher.Daemonsets[daemonsetName]-- + } + } + } + clusterWatcher.DaemonsetsLock.Unlock() + + if deleteDaemonSet { + err := clusterWatcher.Client.AppsV1().DaemonSets(common.Namespace).Delete(context.Background(), daemonsetName, v1.DeleteOptions{}) + if err != nil { + clusterWatcher.Log.Warnf("Cannot delete daemonset %s, error=%s", daemonsetName, err.Error()) + } + } + if newDaemonSet { + daemonset := generateDaemonset(daemonsetName, enforcer, runtime, socket, runtimeStorage, kernelVersion) + _, err := clusterWatcher.Client.AppsV1().DaemonSets(common.Namespace).Create(context.Background(), daemonset, v1.CreateOptions{}) + if err != nil { + clusterWatcher.Log.Warnf("Cannot Create daemonset %s, error=%s", daemonsetName, err.Error()) + } + } + +} + +func (clusterWatcher *ClusterWatcher) WatchConfigCrd() { + + factory := opv1Informer.NewSharedInformerFactoryWithOptions(clusterWatcher.Opv1Client, + time.Duration(5*time.Second), + opv1Informer.WithNamespace(common.Namespace)) + + informer := factory.Operator().V1().KubeArmorConfigs().Informer() + + if informer == nil { + clusterWatcher.Log.Warn("Failed to initialize KubeArmorConfig informer") + return + } + + var firstRun = true + + informer.AddEventHandler( + cache.ResourceEventHandlerFuncs{ + AddFunc: func(obj interface{}) { + configCrdList, err := clusterWatcher.Opv1Client.OperatorV1().KubeArmorConfigs(common.Namespace).List(context.Background(), metav1.ListOptions{}) + if err != nil { + clusterWatcher.Log.Warn("Failed to list Operator Config CRs") + return + } + for _, cfg := range configCrdList.Items { + // if there's any crd with Running status + // mark it as current operating config crd + if cfg.Status.Phase == common.RUNNING { + common.OperatigConfigCrd = &cfg + if firstRun { + go clusterWatcher.WatchRequiredResources() + firstRun = false + } + break + } + } + if cfg, ok := obj.(*opv1.KubeArmorConfig); ok { + // if there's no operating crd exist + if common.OperatigConfigCrd == nil { + common.OperatigConfigCrd = cfg + UpdateConfigMapData(&cfg.Spec) + UpdateImages(&cfg.Spec) + // update status to (Installation) Created + go clusterWatcher.UpdateCrdStatus(cfg.Name, common.CREATED, common.CREATED_MSG) + go clusterWatcher.WatchRequiredResources() + firstRun = false + } + // if it's not the operating crd + // update this crd status as Error and return + if cfg.Name != common.OperatigConfigCrd.Name { + go clusterWatcher.UpdateCrdStatus(cfg.Name, common.ERROR, common.MULTIPLE_CRD_ERR_MSG) + return + } + + } + }, + UpdateFunc: func(oldObj, newObj interface{}) { + if cfg, ok := newObj.(*opv1.KubeArmorConfig); ok { + // update configmap only if it's operating crd + if common.OperatigConfigCrd != nil && cfg.Name == common.OperatigConfigCrd.Name { + configChanged := UpdateConfigMapData(&cfg.Spec) + imageUpdated := UpdateImages(&cfg.Spec) + // return if only status has been updated + if !configChanged && cfg.Status != oldObj.(*opv1.KubeArmorConfig).Status && len(imageUpdated) < 1 { + return + } + if len(imageUpdated) > 0 { + clusterWatcher.UpdateKubeArmorImages(imageUpdated) + } + if configChanged { + // update status to Updating + go clusterWatcher.UpdateCrdStatus(cfg.Name, common.UPDATING, common.UPDATING_MSG) + clusterWatcher.UpdateKubeArmorConfigMap(cfg) + } + } + } + }, + DeleteFunc: func(obj interface{}) { + if cfg, ok := obj.(*opv1.KubeArmorConfig); ok { + if common.OperatigConfigCrd != nil && cfg.Name == common.OperatigConfigCrd.Name { + common.OperatigConfigCrd = nil + } + } + }, + }, + ) + + go informer.Run(wait.NeverStop) + + if ok := cache.WaitForCacheSync(wait.NeverStop, informer.HasSynced); !ok { + clusterWatcher.Log.Warn("Failed to wait for cache sync") + } +} + +func (clusterWatcher *ClusterWatcher) UpdateKubeArmorImages(images []string) error { + var res error + for _, img := range images { + switch img { + case "kubearmor", "init": + dsList, err := clusterWatcher.Client.AppsV1().DaemonSets(common.Namespace).List(context.Background(), v1.ListOptions{ + LabelSelector: "kubearmor-app=kubearmor", + }) + if err != nil { + clusterWatcher.Log.Warnf("Cannot list KubeArmor daemonset(s) error=%s", err.Error()) + res = err + } else { + for _, ds := range dsList.Items { + ds.Spec.Template.Spec.Containers[0].Image = common.KubeArmorImage + ds.Spec.Template.Spec.Containers[0].ImagePullPolicy = corev1.PullPolicy(common.KubeArmorInitImagePullPolicy) + ds.Spec.Template.Spec.InitContainers[0].Image = common.KubeArmorInitImage + ds.Spec.Template.Spec.InitContainers[0].ImagePullPolicy = corev1.PullPolicy(common.KubeArmorInitImagePullPolicy) + _, err = clusterWatcher.Client.AppsV1().DaemonSets(common.Namespace).Update(context.Background(), &ds, v1.UpdateOptions{}) + if err != nil { + clusterWatcher.Log.Warnf("Cannot update daemonset=%s error=%s", ds.Name, err.Error()) + res = err + } else { + clusterWatcher.Log.Infof("Updated daemonset=%s", ds.Name) + } + } + } + case "relay": + relay, err := clusterWatcher.Client.AppsV1().Deployments(common.Namespace).Get(context.Background(), deployments.RelayDeploymentName, v1.GetOptions{}) + if err != nil { + clusterWatcher.Log.Warnf("Cannot get deployment=%s error=%s", deployments.RelayDeploymentName, err.Error()) + res = err + } else { + relay.Spec.Template.Spec.Containers[0].Image = common.KubeArmorRelayImage + relay.Spec.Template.Spec.Containers[0].ImagePullPolicy = corev1.PullPolicy(common.KubeArmorRelayImagePullPolicy) + _, err = clusterWatcher.Client.AppsV1().Deployments(common.Namespace).Update(context.Background(), relay, v1.UpdateOptions{}) + if err != nil { + clusterWatcher.Log.Warnf("Cannot update deployment=%s error=%s", deployments.RelayDeploymentName, err.Error()) + res = err + } else { + clusterWatcher.Log.Infof("Updated Deployment=%s with image=%s", deployments.RelayDeploymentName, common.KubeArmorRelayImage) + } + } + + case "controller", "rbac": + controller, err := clusterWatcher.Client.AppsV1().Deployments(common.Namespace).Get(context.Background(), deployments.KubeArmorControllerDeploymentName, v1.GetOptions{}) + if err != nil { + clusterWatcher.Log.Warnf("Cannot get deployment=%s error=%s", deployments.KubeArmorControllerDeploymentName, err.Error()) + res = err + } else { + containers := &controller.Spec.Template.Spec.Containers + for i, container := range *containers { + if container.Name == "manager" { + (*containers)[i].Image = common.KubeArmorControllerImage + (*containers)[i].ImagePullPolicy = corev1.PullPolicy(common.KubeArmorControllerImagePullPolicy) + } else { + (*containers)[i].Image = common.KubeRbacProxyImage + } + } + _, err = clusterWatcher.Client.AppsV1().Deployments(common.Namespace).Update(context.Background(), controller, v1.UpdateOptions{}) + if err != nil { + clusterWatcher.Log.Warnf("Cannot update deployment=%s error=%s", deployments.KubeArmorControllerDeploymentName, err.Error()) + res = err + } else { + clusterWatcher.Log.Infof("Updated Deployment=%s", deployments.KubeArmorControllerDeploymentName) + } + } + } + } + + return res +} + +func UpdateIfDefinedAndUpdated(common *string, in string) bool { + if in != "" && in != *common { + *common = in + return true + } + return false +} + +func UpdateImages(config *opv1.KubeArmorConfigSpec) []string { + updatedImages := []string{} + // if kubearmor image or imagePullPolicy got updated + if UpdateIfDefinedAndUpdated(&common.KubeArmorImage, config.KubeArmorImage.Image) || + UpdateIfDefinedAndUpdated(&common.KubeArmorImagePullPolicy, config.KubeArmorImage.ImagePullPolicy) { + updatedImages = append(updatedImages, "kubearmor") + } + // if kubearmor-init image or imagePullPolicy got updated + if UpdateIfDefinedAndUpdated(&common.KubeArmorInitImage, config.KubeArmorInitImage.Image) || + UpdateIfDefinedAndUpdated(&common.KubeArmorInitImagePullPolicy, config.KubeArmorInitImage.ImagePullPolicy) { + updatedImages = append(updatedImages, "init") + } + // kubearmor-relay image or imagePullPolicy got updated + if UpdateIfDefinedAndUpdated(&common.KubeArmorRelayImage, config.KubeArmorRelayImage.Image) || + UpdateIfDefinedAndUpdated(&common.KubeArmorRelayImagePullPolicy, config.KubeArmorRelayImage.ImagePullPolicy) { + updatedImages = append(updatedImages, "relay") + } + // if kubearmor-controller image or imagePullPolicy got updated + if UpdateIfDefinedAndUpdated(&common.KubeArmorControllerImage, config.KubeArmorControllerImage.Image) || + UpdateIfDefinedAndUpdated(&common.KubeArmorControllerImagePullPolicy, config.KubeArmorControllerImage.ImagePullPolicy) { + updatedImages = append(updatedImages, "controller") + } + // if kube-rbac-proxy image or imagePullPolicy got updated + if UpdateIfDefinedAndUpdated(&common.KubeRbacProxyImage, config.KubeRbacProxyImage.Image) || + UpdateIfDefinedAndUpdated(&common.KubeRbacProxyImagePullPolicy, config.KubeRbacProxyImage.ImagePullPolicy) { + updatedImages = append(updatedImages, "rbac") + } + return updatedImages +} + +func (clusterWatcher *ClusterWatcher) UpdateCrdStatus(cfg, phase, message string) { + err := wait.ExponentialBackoff(wait.Backoff{Steps: 5, Duration: 500 * time.Millisecond}, func() (bool, error) { + configCrd, err := clusterWatcher.Opv1Client.OperatorV1().KubeArmorConfigs(common.Namespace).Get(context.Background(), cfg, metav1.GetOptions{}) + if err != nil { + // retry the update + return false, nil + } + newStatus := opv1.KubeArmorConfigStatus{ + Phase: phase, + Message: message, + } + // update status only if there's any change + if configCrd.Status != newStatus { + configCrd.Status = newStatus + _, err = clusterWatcher.Opv1Client.OperatorV1().KubeArmorConfigs(common.Namespace).UpdateStatus(context.Background(), configCrd, metav1.UpdateOptions{}) + if err != nil { + // retry the update + return false, nil + } + } + return true, nil + }) + if err != nil { + clusterWatcher.Log.Errorf("Error updating the ConfigCR status %s", err) + return + } + clusterWatcher.Log.Info("Config CR Status Updated Successfully") +} + +func (clusterWatcher *ClusterWatcher) UpdateKubeArmorConfigMap(cfg *opv1.KubeArmorConfig) { + err := wait.ExponentialBackoff(wait.Backoff{Steps: 5, Duration: 500 * time.Millisecond}, func() (bool, error) { + cm, err := clusterWatcher.Client.CoreV1().ConfigMaps(common.Namespace).Get(context.Background(), deployments.KubeArmorConfigMapName, metav1.GetOptions{}) + if err != nil { + if isNotfound(err) { + return true, nil + } + // retry the update + return false, nil + } + cm.Data = common.ConfigMapData + _, err = clusterWatcher.Client.CoreV1().ConfigMaps(common.Namespace).Update(context.Background(), cm, metav1.UpdateOptions{}) + if err != nil { + // retry the update + return false, nil + } + return true, nil + }) + + if err != nil { + clusterWatcher.Log.Errorf("Error updating the KubeArmor Configmap %s", err) + go clusterWatcher.UpdateCrdStatus(cfg.Name, common.ERROR, common.UPDATION_FAILED_ERR_MSG) + return + } + go clusterWatcher.UpdateCrdStatus(cfg.Name, common.RUNNING, common.RUNNING_MSG) + clusterWatcher.Log.Info("KubeArmor Config Updated Successfully") +} + +func UpdateConfigMapData(config *opv1.KubeArmorConfigSpec) bool { + updated := false + if config.DefaultFilePosture != "" { + if common.ConfigMapData[common.ConfigDefaultFilePosture] != string(config.DefaultFilePosture) { + common.ConfigMapData[common.ConfigDefaultFilePosture] = string(config.DefaultFilePosture) + updated = true + } + } + if config.DefaultCapabilitiesPosture != "" { + if common.ConfigMapData[common.ConfigDefaultCapabilitiesPosture] != string(config.DefaultCapabilitiesPosture) { + common.ConfigMapData[common.ConfigDefaultCapabilitiesPosture] = string(config.DefaultCapabilitiesPosture) + updated = true + } + } + if config.DefaultNetworkPosture != "" { + if common.ConfigMapData[common.ConfigDefaultNetworkPosture] != string(config.DefaultNetworkPosture) { + common.ConfigMapData[common.ConfigDefaultNetworkPosture] = string(config.DefaultNetworkPosture) + updated = true + } + } + if config.DefaultVisibility != "" { + if common.ConfigMapData[common.ConfigVisibility] != string(config.DefaultVisibility) { + common.ConfigMapData[common.ConfigVisibility] = string(config.DefaultVisibility) + updated = true + } + } + return updated +} diff --git a/pkg/KubeArmorOperator/internal/controller/resources.go b/pkg/KubeArmorOperator/internal/controller/resources.go new file mode 100644 index 0000000000..f86e154bde --- /dev/null +++ b/pkg/KubeArmorOperator/internal/controller/resources.go @@ -0,0 +1,708 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Authors of KubeArmor + +package controller + +import ( + "bytes" + "context" + "fmt" + "strings" + "time" + + deployments "github.com/kubearmor/KubeArmor/deployments/get" + crds "github.com/kubearmor/KubeArmor/pkg/KubeArmorController/crd" + "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/common" + "golang.org/x/mod/semver" + v1 "k8s.io/api/admissionregistration/v1" + appsv1 "k8s.io/api/apps/v1" + batchv1 "k8s.io/api/batch/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + metav1errors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func generateDaemonset(name, enforcer, runtime, socket, runtimeStorage, kernelVersion string) *appsv1.DaemonSet { + enforcerVolumes, enforcerVolumeMounts := genEnforcerVolumes(enforcer) + runtimeVolumes, runtimeVolumeMounts := genRuntimeVolumes(runtime, socket, runtimeStorage) + vols := []corev1.Volume{} + volMnts := []corev1.VolumeMount{} + vols = append(vols, enforcerVolumes...) + vols = append(vols, runtimeVolumes...) + volMnts = append(volMnts, enforcerVolumeMounts...) + volMnts = append(volMnts, runtimeVolumeMounts...) + commonVols := common.CommonVolumes + commonVolMnts := common.CommonVolumesMount + if isKernelHeaderMountsRequired(kernelVersion) { + commonVols = append(commonVols, common.KernelHeaderVolumes...) + commonVolMnts = append(commonVolMnts, common.KernelHeaderVolumesMount...) + } + vols = append(vols, commonVols...) + volMnts = append(volMnts, commonVolMnts...) + daemonset := deployments.GenerateDaemonSet("generic", common.Namespace) + daemonset.Name = name + labels := map[string]string{ + common.EnforcerLabel: enforcer, + common.RuntimeLabel: runtime, + common.RuntimeStorageLabel: runtimeStorage, + common.SocketLabel: socket, + common.OsLabel: "linux", + } + daemonset.Spec.Template.Spec.NodeSelector = common.CopyStrMap(labels) + labels["kubearmor-app"] = "kubearmor" + daemonset.Spec.Template.Labels = labels + daemonset.Spec.Template.Spec.ServiceAccountName = "kubearmor" + daemonset.Spec.Selector = &metav1.LabelSelector{ + MatchLabels: labels, + } + if deployment_uuid != "" { + daemonset.OwnerReferences = []metav1.OwnerReference{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Name: deployment_name, + UID: deployment_uuid, + }, + } + } + daemonset.Spec.Template.Spec.Volumes = vols + daemonset.Spec.Template.Spec.InitContainers[0].VolumeMounts = commonVolMnts + daemonset.Spec.Template.Spec.Containers[0].VolumeMounts = volMnts + daemonset.Spec.Template.Spec.Containers[0].Args = append(daemonset.Spec.Template.Spec.Containers[0].Args, "-criSocket=unix:///"+strings.ReplaceAll(socket, "_", "/")) + // update images + daemonset.Spec.Template.Spec.Containers[0].Image = common.KubeArmorImage + daemonset.Spec.Template.Spec.Containers[0].ImagePullPolicy = corev1.PullPolicy(common.KubeArmorImagePullPolicy) + daemonset.Spec.Template.Spec.InitContainers[0].Image = common.KubeArmorInitImage + daemonset.Spec.Template.Spec.InitContainers[0].ImagePullPolicy = corev1.PullPolicy(common.KubeArmorInitImagePullPolicy) + + daemonset = addOwnership(daemonset).(*appsv1.DaemonSet) + fmt.Printf("generated daemonset: %v", daemonset) + return daemonset +} + +func isKernelHeaderMountsRequired(version string) bool { + target := "v5.2" + switch semver.Compare("v"+version, target) { + case 1: + return false + case -1: + return true + } + return false +} + +func genEnforcerVolumes(enforcer string) (vol []corev1.Volume, volMnt []corev1.VolumeMount) { + if enforcer == "none" { + return nil, nil + } + for _, e := range strings.Split(enforcer, ".") { + vol = append(vol, common.EnforcerVolumes[e]...) + volMnt = append(volMnt, common.EnforcerVolumesMounts[e]...) + } + return +} + +func genRuntimeVolumes(runtime, runtimeSocket, runtimeStorage string) (vol []corev1.Volume, volMnt []corev1.VolumeMount) { + // lookup socket + for _, socket := range common.ContainerRuntimeSocketMap[runtime] { + if strings.ReplaceAll(socket[1:], "/", "_") == runtimeSocket { + vol = append(vol, corev1.Volume{ + Name: runtime + "-socket", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: socket, + Type: &common.HostPathSocket, + }, + }, + }) + volMnt = append(volMnt, corev1.VolumeMount{ + Name: runtime + "-socket", + MountPath: socket, + ReadOnly: true, + }) + break + } + } + // lookup runtime storage location + + for _, storageLocation := range common.RuntimeStorageVolumes[runtime] { + if strings.ReplaceAll(storageLocation[1:], "/", "_") == runtimeStorage { + vol = append(vol, corev1.Volume{ + Name: runtime + "-storage", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: storageLocation, + Type: &common.HostPathDirectory, + }, + }, + }) + volMnt = append(volMnt, corev1.VolumeMount{ + Name: runtime + "-storage", + MountPath: storageLocation, + MountPropagation: &common.HostToContainerMountPropagation, + ReadOnly: true, + }) + break + } + } + return +} + +func genSnitchRole() *rbacv1.ClusterRole { + return &rbacv1.ClusterRole{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.KubeArmorSnitchRoleName, + Namespace: common.Namespace, + }, + Rules: []rbacv1.PolicyRule{ + { + APIGroups: []string{""}, + Verbs: []string{ + "get", + "patch", + }, + Resources: []string{ + "nodes", + }, + }, + }, + } +} + +func genSnitchRoleBinding() *rbacv1.ClusterRoleBinding { + return &rbacv1.ClusterRoleBinding{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.KubeArmorSnitchRoleName + "-binding", + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: common.KubeArmorSnitchRoleName, + Namespace: common.Namespace, + }, + }, + RoleRef: rbacv1.RoleRef{ + APIGroup: "rbac.authorization.k8s.io", + Kind: "ClusterRole", + Name: common.KubeArmorSnitchRoleName, + }, + } +} + +func genSnitchServiceAccount() *corev1.ServiceAccount { + return &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: common.KubeArmorSnitchRoleName, + Namespace: common.Namespace, + }, + } +} + +func deploySnitch(nodename string, runtime string) *batchv1.Job { + job := batchv1.Job{} + job = *addOwnership(&job).(*batchv1.Job) + ttls := int32(100) + job.GenerateName = "kubearmor-snitch-" + + job.Spec = batchv1.JobSpec{ + TTLSecondsAfterFinished: &ttls, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "kubearmor-app": common.KubeArmorSnitchRoleName, + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "snitch", + Image: common.OperatorImage, + Command: []string{ + "/operator", + "snitch", + }, + Args: []string{ + "--nodename=$(NODE_NAME)", + "--pathprefix=" + PathPrefix, + "--runtime=" + runtime, + }, + Env: []corev1.EnvVar{ + { + Name: "NODE_NAME", + ValueFrom: &corev1.EnvVarSource{FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "spec.nodeName", + }}, + }, + }, + ImagePullPolicy: corev1.PullIfNotPresent, + VolumeMounts: []corev1.VolumeMount{ + { + Name: "rootfs", + MountPath: PathPrefix, + ReadOnly: true, + }, + }, + SecurityContext: &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{ + Add: []corev1.Capability{ + "IPC_LOCK", + "SYS_ADMIN", + "SYS_RESOURCE", + }, + }, + }, + }, + }, + NodeName: nodename, + RestartPolicy: corev1.RestartPolicyOnFailure, + ServiceAccountName: common.KubeArmorSnitchRoleName, + Volumes: []corev1.Volume{ + { + Name: "rootfs", + VolumeSource: corev1.VolumeSource{ + HostPath: &corev1.HostPathVolumeSource{ + Path: "/", + Type: &common.HostPathDirectory, + }, + }, + }, + }, + }, + }, + } + return &job +} + +func isNotfound(err error) bool { + return err != nil && strings.Contains(err.Error(), "not found") +} + +func isAlreadyExists(err error) bool { + return err != nil && strings.Contains(err.Error(), "already exist") +} + +func addOwnership(obj interface{}) interface{} { + if deployment_uuid == "" { + return obj + } + OwnerReferences := []metav1.OwnerReference{ + { + APIVersion: "apps/v1", + Kind: "Deployment", + Name: deployment_name, + UID: deployment_uuid, + }, + } + switch resource := obj.(type) { + case *corev1.ServiceAccount: + resource.OwnerReferences = OwnerReferences + return resource + case *corev1.Service: + resource.OwnerReferences = OwnerReferences + return resource + case *appsv1.Deployment: + resource.OwnerReferences = OwnerReferences + return resource + case *corev1.Secret: + resource.OwnerReferences = OwnerReferences + return resource + case *appsv1.DaemonSet: + resource.OwnerReferences = OwnerReferences + return resource + case *batchv1.Job: + resource.OwnerReferences = OwnerReferences + return resource + case *extv1.CustomResourceDefinition: + resource.OwnerReferences = OwnerReferences + return resource + case *corev1.ConfigMap: + resource.OwnerReferences = OwnerReferences + return resource + } + return obj +} + +func (clusterWatcher *ClusterWatcher) AreAllNodesProcessed() bool { + processedNodes := 0 + clusterWatcher.DaemonsetsLock.Lock() + clusterWatcher.NodesLock.Lock() + + processedNodes = len(clusterWatcher.Nodes) + dsCount := 0 + for _, ds := range clusterWatcher.Daemonsets { + if ds > 0 { + dsCount += ds + } + } + + clusterWatcher.NodesLock.Unlock() + clusterWatcher.DaemonsetsLock.Unlock() + + nodes, err := clusterWatcher.Client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{}) + if err != nil { + clusterWatcher.Log.Warnf("Cannot list nodes, error=%s", err.Error()) + return false + } + if !(len(nodes.Items) == processedNodes) { + return false + } + kaPodsList, err := clusterWatcher.Client.CoreV1().Pods(common.Namespace).List(context.Background(), metav1.ListOptions{ + LabelSelector: "kubearmor-app=kubearmor", + }) + return len(kaPodsList.Items) == dsCount + +} + +func (clusterWatcher *ClusterWatcher) WatchRequiredResources() { + var caCert, tlsCrt, tlsKey *bytes.Buffer + var kGenErr, err, installErr error + RotateTls := false + FirstRun := true + srvAccs := []*corev1.ServiceAccount{ + addOwnership(deployments.GetServiceAccount(common.Namespace)).(*corev1.ServiceAccount), + addOwnership(deployments.GetKubeArmorControllerServiceAccount(common.Namespace)).(*corev1.ServiceAccount), + addOwnership(genSnitchServiceAccount()).(*corev1.ServiceAccount), + } + clusterRoles := []*rbacv1.ClusterRole{ + addOwnership(genSnitchRole()).(*rbacv1.ClusterRole), + addOwnership(deployments.GetClusterRole()).(*rbacv1.ClusterRole), + addOwnership(deployments.GetKubeArmorControllerProxyRole()).(*rbacv1.ClusterRole), + addOwnership(deployments.GetKubeArmorControllerClusterRole()).(*rbacv1.ClusterRole), + } + clusterRoleBindings := []*rbacv1.ClusterRoleBinding{ + addOwnership(deployments.GetClusterRoleBinding(common.Namespace)).(*rbacv1.ClusterRoleBinding), + addOwnership(deployments.GetKubeArmorControllerClusterRoleBinding(common.Namespace)).(*rbacv1.ClusterRoleBinding), + addOwnership(deployments.GetKubeArmorControllerProxyRoleBinding(common.Namespace)).(*rbacv1.ClusterRoleBinding), + addOwnership(genSnitchRoleBinding()).(*rbacv1.ClusterRoleBinding), + } + roles := []*rbacv1.Role{ + addOwnership(deployments.GetKubeArmorControllerLeaderElectionRole(common.Namespace)).(*rbacv1.Role), + } + roleBindings := []*rbacv1.RoleBinding{ + addOwnership(deployments.GetKubeArmorControllerLeaderElectionRoleBinding(common.Namespace)).(*rbacv1.RoleBinding), + } + + svcs := []*corev1.Service{ + addOwnership(deployments.GetKubeArmorControllerMetricsService(common.Namespace)).(*corev1.Service), + addOwnership(deployments.GetKubeArmorControllerWebhookService(common.Namespace)).(*corev1.Service), + addOwnership(deployments.GetRelayService(common.Namespace)).(*corev1.Service), + } + // Install CRDs + ksp := crds.GetKspCRD() + ksp = addOwnership(ksp).(extv1.CustomResourceDefinition) + if _, err := clusterWatcher.ExtClient.ApiextensionsV1().CustomResourceDefinitions().Create(context.Background(), &ksp, metav1.CreateOptions{}); err != nil && !metav1errors.IsAlreadyExists(err) { + if !isAlreadyExists(err) { + installErr = err + clusterWatcher.Log.Warnf("Cannot install Ksp CRD, error=%s", err.Error()) + } + } + hsp := crds.GetHspCRD() + hsp = addOwnership(hsp).(extv1.CustomResourceDefinition) + if _, err := clusterWatcher.ExtClient.ApiextensionsV1().CustomResourceDefinitions().Create(context.Background(), &hsp, metav1.CreateOptions{}); err != nil && !metav1errors.IsAlreadyExists(err) { + if !isAlreadyExists(err) { + installErr = err + clusterWatcher.Log.Warnf("Cannot install Hsp CRD, error=%s", err.Error()) + } + } + // kubearmor-controller and relay-server deployments + controller := deployments.GetKubeArmorControllerDeployment(common.Namespace) + relayServer := deployments.GetRelayDeployment(common.Namespace) + + // update images + containers := &controller.Spec.Template.Spec.Containers + for i, container := range *containers { + if container.Name == "manager" { + (*containers)[i].Image = common.KubeArmorControllerImage + (*containers)[i].ImagePullPolicy = corev1.PullPolicy(common.KubeArmorControllerImagePullPolicy) + } else { + (*containers)[i].Image = common.KubeRbacProxyImage + (*containers)[i].ImagePullPolicy = corev1.PullPolicy(common.KubeRbacProxyImagePullPolicy) + } + } + relayServer.Spec.Template.Spec.Containers[0].Image = common.KubeArmorRelayImage + relayServer.Spec.Template.Spec.Containers[0].ImagePullPolicy = corev1.PullPolicy(common.KubeArmorRelayImagePullPolicy) + deploys := []*appsv1.Deployment{ + addOwnership(controller).(*appsv1.Deployment), + addOwnership(relayServer).(*appsv1.Deployment), + } + + // kubearmor configmap + configmap := addOwnership(deployments.GetKubearmorConfigMap(common.Namespace, deployments.KubeArmorConfigMapName)).(*corev1.ConfigMap) + configmap.Data = common.ConfigMapData + + for { + caCert, tlsCrt, tlsKey, kGenErr = common.GeneratePki(common.Namespace, deployments.KubeArmorControllerWebhookServiceName) + if kGenErr == nil { + break + } + clusterWatcher.Log.Infof("Couldnt generate TLS secret, re-trying in 3 seconds ...") + time.Sleep(3 * time.Second) + } + + secret := deployments.GetKubeArmorControllerTLSSecret(common.Namespace, caCert.String(), tlsCrt.String(), tlsKey.String()) + secret = addOwnership(secret).(*corev1.Secret) + mutationhook := deployments.GetKubeArmorControllerMutationAdmissionConfiguration(common.Namespace, caCert.Bytes()) + mutationhook = addOwnership(mutationhook).(*v1.MutatingWebhookConfiguration) + var caInK8sSecret []byte + for { + for _, srvAcc := range srvAccs { + _, err = clusterWatcher.Client.CoreV1().ServiceAccounts(common.Namespace).Get(context.Background(), srvAcc.Name, metav1.GetOptions{}) + if isNotfound(err) { + clusterWatcher.Log.Infof("Creating service account %s", srvAcc.Name) + _, err := clusterWatcher.Client.CoreV1().ServiceAccounts(common.Namespace).Create(context.Background(), srvAcc, metav1.CreateOptions{}) + if err != nil { + installErr = err + clusterWatcher.Log.Warnf("Cannot create service account %s, error=%s", srvAcc.Name, err.Error()) + } + + } + } + + //rbac + for _, role := range roles { + _, err = clusterWatcher.Client.RbacV1().Roles(common.Namespace).Get(context.Background(), role.Name, metav1.GetOptions{}) + if isNotfound(err) { + clusterWatcher.Log.Infof("Creating role %s", role.Name) + _, err := clusterWatcher.Client.RbacV1().Roles(common.Namespace).Create(context.Background(), role, metav1.CreateOptions{}) + if err != nil { + installErr = err + clusterWatcher.Log.Warnf("Cannot create role %s, error=%s", role.Name, err.Error()) + } + } + } + + for _, binding := range roleBindings { + _, err = clusterWatcher.Client.RbacV1().RoleBindings(common.Namespace).Get(context.Background(), binding.Name, metav1.GetOptions{}) + if isNotfound(err) { + clusterWatcher.Log.Infof("Creating role binding %s", binding.Name) + _, err := clusterWatcher.Client.RbacV1().RoleBindings(common.Namespace).Create(context.Background(), binding, metav1.CreateOptions{}) + if err != nil { + installErr = err + clusterWatcher.Log.Warnf("Cannot create role binding %s, error=%s", binding.Name, err.Error()) + } + } + } + + for _, clusterRole := range clusterRoles { + _, err = clusterWatcher.Client.RbacV1().ClusterRoles().Get(context.Background(), clusterRole.Name, metav1.GetOptions{}) + if isNotfound(err) { + clusterWatcher.Log.Infof("Creating cluster role %s", clusterRole.Name) + _, err := clusterWatcher.Client.RbacV1().ClusterRoles().Create(context.Background(), clusterRole, metav1.CreateOptions{}) + if err != nil { + installErr = err + clusterWatcher.Log.Warnf("Cannot create cluster role %s, error=%s", clusterRole.Name, err.Error()) + } + } + } + + for _, binding := range clusterRoleBindings { + _, err = clusterWatcher.Client.RbacV1().ClusterRoleBindings().Get(context.Background(), binding.Name, metav1.GetOptions{}) + if isNotfound(err) { + clusterWatcher.Log.Infof("Creating cluster role binding %s", binding.Name) + _, err := clusterWatcher.Client.RbacV1().ClusterRoleBindings().Create(context.Background(), binding, metav1.CreateOptions{}) + if err != nil { + installErr = err + clusterWatcher.Log.Warnf("Cannot create cluster role binding %s, error=%s", binding.Name, err.Error()) + } + } + } + + //configmap + _, err := clusterWatcher.Client.CoreV1().ConfigMaps(common.Namespace).Get(context.Background(), configmap.Name, metav1.GetOptions{}) + if isNotfound(err) { + clusterWatcher.Log.Infof("Creating ConfigMap %s", configmap.Name) + _, err := clusterWatcher.Client.CoreV1().ConfigMaps(common.Namespace).Create(context.Background(), configmap, metav1.CreateOptions{}) + if err != nil { + installErr = err + clusterWatcher.Log.Warnf("Cannot create configmap %s, error=%s", configmap.Name, err.Error()) + } + } + + // svcs + for _, svc := range svcs { + _, err = clusterWatcher.Client.CoreV1().Services(common.Namespace).Get(context.Background(), svc.Name, metav1.GetOptions{}) + if isNotfound(err) { + clusterWatcher.Log.Infof("Creating service %s", svc.Name) + _, err := clusterWatcher.Client.CoreV1().Services(common.Namespace).Create(context.Background(), svc, metav1.CreateOptions{}) + if err != nil { + installErr = err + clusterWatcher.Log.Warnf("Cannot create service %s, error=%s", svc.Name, err.Error()) + } + } + } + + //secret + s, err := clusterWatcher.Client.CoreV1().Secrets(common.Namespace).Get(context.Background(), secret.Name, metav1.GetOptions{}) + if isNotfound(err) { + clusterWatcher.Log.Infof("Creating secret %s", secret.Name) + _, err := clusterWatcher.Client.CoreV1().Secrets(common.Namespace).Create(context.Background(), secret, metav1.CreateOptions{}) + if err != nil { + installErr = err + clusterWatcher.Log.Warnf("Cannot create secret %s, error=%s", secret.Name, err.Error()) + } else { + RotateTls = true && !FirstRun + if !FirstRun { + clusterWatcher.Log.Warnf("Secret was created, rotating TLS secrets") + } + } + } else if err == nil { + caInK8sSecret = s.Data["ca.crt"] + } + + // deploy + for _, deploy := range deploys { + _, err := clusterWatcher.Client.AppsV1().Deployments(common.Namespace).Get(context.Background(), deploy.Name, metav1.GetOptions{}) + if isNotfound(err) { + clusterWatcher.Log.Infof("Creating deployment %s", deploy.Name) + _, err = clusterWatcher.Client.AppsV1().Deployments(common.Namespace).Create(context.Background(), deploy, metav1.CreateOptions{}) + if err != nil { + installErr = err + clusterWatcher.Log.Warnf("Cannot create deployment %s, error=%s", deploy.Name, err.Error()) + } + } + } + + //mutation webhook + hook, err := clusterWatcher.Client.AdmissionregistrationV1().MutatingWebhookConfigurations().Get(context.Background(), mutationhook.Name, metav1.GetOptions{}) + if isNotfound(err) { + clusterWatcher.Log.Infof("Creating mutation webhook %s", mutationhook.Name) + _, err = clusterWatcher.Client.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(context.Background(), mutationhook, metav1.CreateOptions{}) + if err != nil { + installErr = err + clusterWatcher.Log.Warnf("Cannot create mutation webhook %s, error=%s", mutationhook.Name, err.Error()) + } + } else if err == nil { + if !bytes.Equal(hook.Webhooks[0].ClientConfig.CABundle, caInK8sSecret) { + // rotate + RotateTls = true && !FirstRun + if !FirstRun { + clusterWatcher.Log.Warnf("mutation CA cert does not match secret CA cert, rotating tls secrets") + } + } + } else { + installErr = err + clusterWatcher.Log.Error(err.Error()) + } + + // update operatingConfigCrd status to Running + if common.OperatigConfigCrd != nil { + if installErr != nil { + installErr = nil + go clusterWatcher.UpdateCrdStatus(common.OperatigConfigCrd.Name, common.ERROR, common.INSTALLATION_ERR_MSG) + } else if clusterWatcher.AreAllNodesProcessed() { + go clusterWatcher.UpdateCrdStatus(common.OperatigConfigCrd.Name, common.RUNNING, common.RUNNING_MSG) + } else { + go clusterWatcher.UpdateCrdStatus(common.OperatigConfigCrd.Name, common.PENDING, common.PENDING_MSG) + } + } + + if RotateTls { + clusterWatcher.RotateTlsCerts() + RotateTls = false + } + if FirstRun { + FirstRun = false + } + time.Sleep(10 * time.Second) + } +} + +func (clusterWatcher *ClusterWatcher) RotateTlsCerts() { + var suffix string + var caCert, tlsCrt, tlsKey *bytes.Buffer + var err error + retries := 0 + for { + if retries == 3 { + return + } + retries++ + suffix, err = common.GetFreeRandSuffix(clusterWatcher.Client, common.Namespace) + if err == nil { + clusterWatcher.Log.Infof("Using suffix %s for all new temorary resources", suffix) + break + } + clusterWatcher.Log.Infof("Cannot find a suffix, err=%s, retrying in 3 seconds...", err.Error()) + time.Sleep(3 * time.Second) + } + serviceName := deployments.KubeArmorControllerWebhookServiceName + "-" + suffix + for { + caCert, tlsCrt, tlsKey, err = common.GeneratePki(common.Namespace, serviceName) + if err == nil { + break + } + clusterWatcher.Log.Infof("Could'nt generate TLS secret, retrying in 3 seconds") + time.Sleep(3 * time.Second) + } + tmpsecret := deployments.GetKubeArmorControllerTLSSecret(common.Namespace, caCert.String(), tlsCrt.String(), tlsKey.String()) + tmpsecret = addOwnership(tmpsecret).(*corev1.Secret) + tmpsecret.Name = tmpsecret.GetName() + "-" + suffix + _, err = clusterWatcher.Client.CoreV1().Secrets(common.Namespace).Create(context.Background(), tmpsecret, metav1.CreateOptions{}) + if err != nil { + clusterWatcher.Log.Warnf("Cannot create secret %s, error=%s", tmpsecret.Name, err.Error()) + } + tmpdeploy := deployments.GetKubeArmorControllerDeployment(common.Namespace) + tmpdeploy = addOwnership(tmpdeploy).(*appsv1.Deployment) + tmpdeploy.Name = tmpdeploy.GetName() + "-" + suffix + for i, s := range tmpdeploy.Spec.Template.Spec.Volumes { + if s.Name == "cert" { + s.Secret.SecretName = tmpsecret.GetName() + tmpdeploy.Spec.Template.Spec.Volumes[i] = s + break + } + } + selectLabels := tmpdeploy.Spec.Selector.MatchLabels + selectLabels["kubearmor-app"] = suffix + tmpdeploy.Spec.Selector.MatchLabels = selectLabels + origdeploy, _ := clusterWatcher.Client.AppsV1().Deployments(common.Namespace).Get(context.Background(), deployments.KubeArmorControllerDeploymentName, metav1.GetOptions{}) + origdeploy = addOwnership(origdeploy).(*appsv1.Deployment) + tmpdeploy.Spec.Replicas = origdeploy.Spec.Replicas + if _, err := clusterWatcher.Client.AppsV1().Deployments(common.Namespace).Create(context.Background(), tmpdeploy, metav1.CreateOptions{}); err != nil { + clusterWatcher.Log.Warnf("Cannot create deployment %s, error=%s", tmpdeploy.Name, err.Error()) + } + + time.Sleep(10 * time.Second) + + tmpservice := deployments.GetKubeArmorControllerWebhookService(common.Namespace) + tmpservice = addOwnership(tmpservice).(*corev1.Service) + tmpservice.Name = serviceName + tmpservice.Spec.Selector = selectLabels + if _, err := clusterWatcher.Client.CoreV1().Services(common.Namespace).Create(context.Background(), tmpservice, metav1.CreateOptions{}); err != nil { + clusterWatcher.Log.Warnf("Cannot create deployment %s, error=%s", tmpservice.Name, err.Error()) + } + tmpmutation := deployments.GetKubeArmorControllerMutationAdmissionConfiguration(common.Namespace, caCert.Bytes()) + mutationName := tmpmutation.Name + tmpmutation = addOwnership(tmpmutation).(*v1.MutatingWebhookConfiguration) + tmpmutation.Name = tmpmutation.Name + "-" + suffix + tmpmutation.Webhooks[0].ClientConfig.Service.Name = tmpservice.GetName() + if _, err := clusterWatcher.Client.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(context.Background(), tmpmutation, metav1.CreateOptions{}); err != nil { + clusterWatcher.Log.Warnf("Cannot create mutation webhook %s, error=%s", tmpmutation.Name, err.Error()) + } + clusterWatcher.Client.AdmissionregistrationV1().MutatingWebhookConfigurations().Delete(context.Background(), mutationName, metav1.DeleteOptions{}) + caCert, tlsCrt, tlsKey, _ = common.GeneratePki(common.Namespace, deployments.KubeArmorControllerWebhookServiceName) + secret := deployments.GetKubeArmorControllerTLSSecret(common.Namespace, caCert.String(), tlsCrt.String(), tlsKey.String()) + secret = addOwnership(secret).(*corev1.Secret) + clusterWatcher.Client.CoreV1().Secrets(common.Namespace).Update(context.Background(), secret, metav1.UpdateOptions{}) + + replicas := int32(0) + origdeploy.Spec.Replicas = &replicas + clusterWatcher.Client.AppsV1().Deployments(common.Namespace).Update(context.Background(), origdeploy, metav1.UpdateOptions{}) + time.Sleep(10 * time.Second) + origdeploy, _ = clusterWatcher.Client.AppsV1().Deployments(common.Namespace).Get(context.Background(), deployments.KubeArmorControllerDeploymentName, metav1.GetOptions{}) + origdeploy = addOwnership(origdeploy).(*appsv1.Deployment) + origdeploy.Spec.Replicas = tmpdeploy.Spec.Replicas + + clusterWatcher.Client.AppsV1().Deployments(common.Namespace).Update(context.Background(), origdeploy, metav1.UpdateOptions{}) + mutation := deployments.GetKubeArmorControllerMutationAdmissionConfiguration(common.Namespace, caCert.Bytes()) + mutation = addOwnership(mutation).(*v1.MutatingWebhookConfiguration) + + clusterWatcher.Client.AdmissionregistrationV1().MutatingWebhookConfigurations().Create(context.Background(), mutation, metav1.CreateOptions{}) + + clusterWatcher.Client.AdmissionregistrationV1().MutatingWebhookConfigurations().Delete(context.Background(), tmpmutation.Name, metav1.DeleteOptions{}) + clusterWatcher.Client.CoreV1().Services(common.Namespace).Delete(context.Background(), tmpservice.Name, metav1.DeleteOptions{}) + clusterWatcher.Client.AppsV1().Deployments(common.Namespace).Delete(context.Background(), tmpdeploy.Name, metav1.DeleteOptions{}) + clusterWatcher.Client.CoreV1().Secrets(common.Namespace).Delete(context.Background(), tmpsecret.Name, metav1.DeleteOptions{}) + clusterWatcher.Log.Info("Tls rotation completed") +} diff --git a/pkg/KubeArmorOperator/k8s/client.go b/pkg/KubeArmorOperator/k8s/client.go new file mode 100644 index 0000000000..a9d18a417a --- /dev/null +++ b/pkg/KubeArmorOperator/k8s/client.go @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Authors of KubeArmor + +package k8s + +import ( + "os" + + opv1client "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/client/clientset/versioned" + "go.uber.org/zap" + apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" + "k8s.io/client-go/kubernetes" + _ "k8s.io/client-go/plugin/pkg/client/auth" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" +) + +func NewClient(log zap.SugaredLogger, kubeconfig string) *kubernetes.Clientset { + var cfg *rest.Config + log.Info("Trying to load InCluster configuration") + inClusterConfig, err := rest.InClusterConfig() + if err == rest.ErrNotInCluster { + log.Info("Not inside a k8s Cluser, Loading kubeconfig") + kubeConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig}, + &clientcmd.ConfigOverrides{}).ClientConfig() + if err != nil { + log.Errorf("Could'nt load configuration from kubeconfig Error=%s", err.Error()) + os.Exit(1) + } + log.Info("Loaded configuration from kubeconfig") + cfg = kubeConfig + } else if err != nil { + log.Errorf("Could'nt load inCluster configuration Error=%s", err.Error()) + os.Exit(1) + + } else { + log.Info("Loaded InCluster configuration") + cfg = inClusterConfig + } + + client, err := kubernetes.NewForConfig(cfg) + if err != nil { + log.Errorf("Could'nt create k8s clientset Error=%s", err.Error()) + os.Exit(1) + } + + return client +} + +func NewExtClient(log zap.SugaredLogger, kubeconfig string) *apiextensionsclientset.Clientset { + var cfg *rest.Config + log.Info("Trying to load InCluster configuration") + inClusterConfig, err := rest.InClusterConfig() + if err == rest.ErrNotInCluster { + log.Info("Not inside a k8s Cluser, Loading kubeconfig") + kubeConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig}, + &clientcmd.ConfigOverrides{}).ClientConfig() + if err != nil { + log.Errorf("Could'nt load configuration from kubeconfig Error=%s", err.Error()) + os.Exit(1) + } + log.Info("Loaded configuration from kubeconfig") + cfg = kubeConfig + } else if err != nil { + log.Errorf("Could'nt load inCluster configuration Error=%s", err.Error()) + os.Exit(1) + + } else { + log.Info("Loaded InCluster configuration") + cfg = inClusterConfig + } + + client, err := apiextensionsclientset.NewForConfig(cfg) + if err != nil { + log.Errorf("Could'nt create k8s extensions clientset Error=%s", err.Error()) + os.Exit(1) + } + + return client +} + +func NewOpv1Client(log zap.SugaredLogger, kubeconfig string) *opv1client.Clientset { + var cfg *rest.Config + log.Info("Trying to load InCluster configuration") + inClusterConfig, err := rest.InClusterConfig() + if err == rest.ErrNotInCluster { + log.Info("Not inside a k8s Cluser, Loading kubeconfig") + kubeConfig, err := clientcmd.NewNonInteractiveDeferredLoadingClientConfig( + &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig}, + &clientcmd.ConfigOverrides{}).ClientConfig() + if err != nil { + log.Errorf("Could'nt load configuration from kubeconfig Error=%s", err.Error()) + os.Exit(1) + } + log.Info("Loaded configuration from kubeconfig") + cfg = kubeConfig + } else if err != nil { + log.Errorf("Could'nt load inCluster configuration Error=%s", err.Error()) + os.Exit(1) + + } else { + log.Info("Loaded InCluster configuration") + cfg = inClusterConfig + } + + client, err := opv1client.NewForConfig(cfg) + if err != nil { + log.Errorf("Could'nt create operatorv1 clientset Error=%s", err.Error()) + os.Exit(1) + } + + if client == nil { + log.Warn("opv1client is nil") + } + + return client +} diff --git a/pkg/KubeArmorOperator/runtime/runtime.go b/pkg/KubeArmorOperator/runtime/runtime.go new file mode 100644 index 0000000000..12b1c21b4c --- /dev/null +++ b/pkg/KubeArmorOperator/runtime/runtime.go @@ -0,0 +1,46 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2022 Authors of KubeArmor + +package runtime + +import ( + "os" + + "github.com/kubearmor/KubeArmor/pkg/KubeArmorOperator/common" + "go.uber.org/zap" +) + +func DetectRuntimeViaMap(pathPrefix string, k8sRuntime string, log zap.SugaredLogger) (string, string) { + log.Infof("Checking for %s socket\n", k8sRuntime) + if k8sRuntime != "" { + for _, path := range common.ContainerRuntimeSocketMap[k8sRuntime] { + if _, err := os.Stat(pathPrefix + path); err == nil || os.IsPermission(err) { + return k8sRuntime, path + } else { + log.Warnf("%s", err) + } + } + } + log.Warn("Could'nt detect k8s runtime localtion, searching for other runtime sockets") + for runtime, paths := range common.ContainerRuntimeSocketMap { + for _, path := range paths { + if _, err := os.Stat(pathPrefix + path); err == nil || os.IsPermission(err) { + return runtime, path + } else { + log.Warnf("%s", err) + } + } + } + log.Warn("Could'nt detect runtime") + return "NA", "NA" +} + +func DetectRuntimeStorage(pathPrefix, runtime string, log zap.SugaredLogger) string { + + for _, storagelocaltion := range common.RuntimeStorageVolumes[runtime] { + if _, err := os.Stat(pathPrefix + storagelocaltion); err == nil { + return storagelocaltion + } + } + return "NA" +}