From 13194296c6458336ec14483f7334a9204703ce31 Mon Sep 17 00:00:00 2001 From: = Date: Thu, 24 Jul 2025 19:50:16 +0530 Subject: [PATCH 1/9] feat: updated secret config --- .../.devcontainer/devcontainer.json | 25 + .../k8-operator/.devcontainer/post-install.sh | 23 + k8-operator/k8-operator/.dockerignore | 3 + .../k8-operator/.github/workflows/lint.yml | 23 + .../.github/workflows/test-e2e.yml | 32 + .../k8-operator/.github/workflows/test.yml | 23 + k8-operator/k8-operator/.gitignore | 27 + k8-operator/k8-operator/.golangci.yml | 52 ++ k8-operator/k8-operator/Dockerfile | 33 + k8-operator/k8-operator/Makefile | 238 ++++++++ k8-operator/k8-operator/PROJECT | 39 ++ k8-operator/k8-operator/README.md | 135 ++++ .../k8-operator/api/v1alpha1/common.go | 149 +++++ .../k8-operator/api/v1alpha1/generators.go | 152 +++++ .../api/v1alpha1/groupversion_info.go | 20 + .../v1alpha1/infisicaldynamicsecret_types.go | 99 +++ .../api/v1alpha1/infisicalpushsecret_types.go | 115 ++++ .../api/v1alpha1/infisicalsecret_types.go | 182 ++++++ .../api/v1alpha1/zz_generated.deepcopy.go | 307 ++++++++++ k8-operator/k8-operator/cmd/main.go | 258 ++++++++ ...crets.infisical.com_clustergenerators.yaml | 96 +++ ...infisical.com_infisicaldynamicsecrets.yaml | 309 ++++++++++ ...ts.infisical.com_infisicalpushsecrets.yaml | 305 +++++++++ ...isical.com_infisicalpushsecretsecrets.yaml | 57 ++ ...ecrets.infisical.com_infisicalsecrets.yaml | 503 +++++++++++++++ .../secrets.infisical.com_passwords.yaml | 79 +++ .../bases/secrets.infisical.com_uuids.yaml | 46 ++ .../k8-operator/config/crd/kustomization.yaml | 18 + .../config/crd/kustomizeconfig.yaml | 19 + .../default/cert_metrics_manager_patch.yaml | 30 + .../config/default/kustomization.yaml | 234 +++++++ .../config/default/manager_metrics_patch.yaml | 4 + .../config/default/metrics_service.yaml | 18 + .../config/manager/kustomization.yaml | 2 + .../k8-operator/config/manager/manager.yaml | 99 +++ .../network-policy/allow-metrics-traffic.yaml | 27 + .../config/network-policy/kustomization.yaml | 2 + .../config/prometheus/kustomization.yaml | 11 + .../config/prometheus/monitor.yaml | 27 + .../config/prometheus/monitor_tls_patch.yaml | 19 + .../infisicaldynamicsecret_admin_role.yaml | 27 + .../infisicaldynamicsecret_editor_role.yaml | 33 + .../infisicaldynamicsecret_viewer_role.yaml | 29 + .../infisicalpushsecretsecret_admin_role.yaml | 27 + ...infisicalpushsecretsecret_editor_role.yaml | 33 + ...infisicalpushsecretsecret_viewer_role.yaml | 29 + .../rbac/infisicalsecret_admin_role.yaml | 27 + .../rbac/infisicalsecret_editor_role.yaml | 33 + .../rbac/infisicalsecret_viewer_role.yaml | 29 + .../config/rbac/kustomization.yaml | 34 ++ .../config/rbac/leader_election_role.yaml | 40 ++ .../rbac/leader_election_role_binding.yaml | 15 + .../config/rbac/metrics_auth_role.yaml | 17 + .../rbac/metrics_auth_role_binding.yaml | 12 + .../config/rbac/metrics_reader_role.yaml | 9 + k8-operator/k8-operator/config/rbac/role.yaml | 38 ++ .../k8-operator/config/rbac/role_binding.yaml | 15 + .../config/rbac/service_account.yaml | 8 + .../config/samples/kustomization.yaml | 6 + ...crets_v1alpha1_infisicaldynamicsecret.yaml | 9 + ...ts_v1alpha1_infisicalpushsecretsecret.yaml | 9 + .../secrets_v1alpha1_infisicalsecret.yaml | 9 + k8-operator/k8-operator/go.mod | 97 +++ k8-operator/k8-operator/go.sum | 254 ++++++++ .../k8-operator/hack/boilerplate.go.txt | 15 + k8-operator/k8-operator/internal/api/api.go | 148 +++++ .../k8-operator/internal/api/models.go | 208 +++++++ .../k8-operator/internal/api/variables.go | 4 + .../internal/constants/constants.go | 42 ++ .../infisicaldynamicsecret_controller.go | 63 ++ .../infisicaldynamicsecret_controller_test.go | 84 +++ .../infisicalpushsecretsecret_controller.go | 63 ++ ...fisicalpushsecretsecret_controller_test.go | 84 +++ .../controller/infisicalsecret_controller.go | 224 +++++++ .../infisicalsecret_controller_test.go | 84 +++ .../internal/controller/suite_test.go | 116 ++++ .../controllerhelpers/controllerhelpers.go | 293 +++++++++ .../internal/controllerutil/util.go | 45 ++ .../k8-operator/internal/crypto/crypto.go | 42 ++ .../internal/generator/generator.go | 1 + .../internal/generator/password.go | 76 +++ .../k8-operator/internal/generator/uuid.go | 10 + .../k8-operator/internal/model/model.go | 37 ++ .../services/infisicalsecret/conditions.go | 100 +++ .../services/infisicalsecret/handler.go | 94 +++ .../services/infisicalsecret/reconciler.go | 577 ++++++++++++++++++ .../services/infisicalsecret/suite_test.go | 64 ++ .../k8-operator/internal/template/base64.go | 18 + .../k8-operator/internal/template/jwk.go | 43 ++ .../k8-operator/internal/template/pem.go | 98 +++ .../internal/template/pem_chain.go | 117 ++++ .../k8-operator/internal/template/pkcs12.go | 144 +++++ .../k8-operator/internal/template/template.go | 67 ++ .../k8-operator/internal/template/yaml.go | 30 + k8-operator/k8-operator/internal/util/auth.go | 490 +++++++++++++++ .../k8-operator/internal/util/helpers.go | 56 ++ .../k8-operator/internal/util/kubernetes.go | 92 +++ .../k8-operator/internal/util/models.go | 13 + .../k8-operator/internal/util/secrets.go | 186 ++++++ k8-operator/k8-operator/internal/util/time.go | 40 ++ .../k8-operator/internal/util/workspace.go | 27 + .../k8-operator/test/e2e/e2e_suite_test.go | 89 +++ k8-operator/k8-operator/test/e2e/e2e_test.go | 330 ++++++++++ k8-operator/k8-operator/test/utils/utils.go | 254 ++++++++ 104 files changed, 9247 insertions(+) create mode 100644 k8-operator/k8-operator/.devcontainer/devcontainer.json create mode 100644 k8-operator/k8-operator/.devcontainer/post-install.sh create mode 100644 k8-operator/k8-operator/.dockerignore create mode 100644 k8-operator/k8-operator/.github/workflows/lint.yml create mode 100644 k8-operator/k8-operator/.github/workflows/test-e2e.yml create mode 100644 k8-operator/k8-operator/.github/workflows/test.yml create mode 100644 k8-operator/k8-operator/.gitignore create mode 100644 k8-operator/k8-operator/.golangci.yml create mode 100644 k8-operator/k8-operator/Dockerfile create mode 100644 k8-operator/k8-operator/Makefile create mode 100644 k8-operator/k8-operator/PROJECT create mode 100644 k8-operator/k8-operator/README.md create mode 100644 k8-operator/k8-operator/api/v1alpha1/common.go create mode 100644 k8-operator/k8-operator/api/v1alpha1/generators.go create mode 100644 k8-operator/k8-operator/api/v1alpha1/groupversion_info.go create mode 100644 k8-operator/k8-operator/api/v1alpha1/infisicaldynamicsecret_types.go create mode 100644 k8-operator/k8-operator/api/v1alpha1/infisicalpushsecret_types.go create mode 100644 k8-operator/k8-operator/api/v1alpha1/infisicalsecret_types.go create mode 100644 k8-operator/k8-operator/api/v1alpha1/zz_generated.deepcopy.go create mode 100644 k8-operator/k8-operator/cmd/main.go create mode 100644 k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_clustergenerators.yaml create mode 100644 k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicaldynamicsecrets.yaml create mode 100644 k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml create mode 100644 k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecretsecrets.yaml create mode 100644 k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml create mode 100644 k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_passwords.yaml create mode 100644 k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_uuids.yaml create mode 100644 k8-operator/k8-operator/config/crd/kustomization.yaml create mode 100644 k8-operator/k8-operator/config/crd/kustomizeconfig.yaml create mode 100644 k8-operator/k8-operator/config/default/cert_metrics_manager_patch.yaml create mode 100644 k8-operator/k8-operator/config/default/kustomization.yaml create mode 100644 k8-operator/k8-operator/config/default/manager_metrics_patch.yaml create mode 100644 k8-operator/k8-operator/config/default/metrics_service.yaml create mode 100644 k8-operator/k8-operator/config/manager/kustomization.yaml create mode 100644 k8-operator/k8-operator/config/manager/manager.yaml create mode 100644 k8-operator/k8-operator/config/network-policy/allow-metrics-traffic.yaml create mode 100644 k8-operator/k8-operator/config/network-policy/kustomization.yaml create mode 100644 k8-operator/k8-operator/config/prometheus/kustomization.yaml create mode 100644 k8-operator/k8-operator/config/prometheus/monitor.yaml create mode 100644 k8-operator/k8-operator/config/prometheus/monitor_tls_patch.yaml create mode 100644 k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_admin_role.yaml create mode 100644 k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_editor_role.yaml create mode 100644 k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_viewer_role.yaml create mode 100644 k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_admin_role.yaml create mode 100644 k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_editor_role.yaml create mode 100644 k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_viewer_role.yaml create mode 100644 k8-operator/k8-operator/config/rbac/infisicalsecret_admin_role.yaml create mode 100644 k8-operator/k8-operator/config/rbac/infisicalsecret_editor_role.yaml create mode 100644 k8-operator/k8-operator/config/rbac/infisicalsecret_viewer_role.yaml create mode 100644 k8-operator/k8-operator/config/rbac/kustomization.yaml create mode 100644 k8-operator/k8-operator/config/rbac/leader_election_role.yaml create mode 100644 k8-operator/k8-operator/config/rbac/leader_election_role_binding.yaml create mode 100644 k8-operator/k8-operator/config/rbac/metrics_auth_role.yaml create mode 100644 k8-operator/k8-operator/config/rbac/metrics_auth_role_binding.yaml create mode 100644 k8-operator/k8-operator/config/rbac/metrics_reader_role.yaml create mode 100644 k8-operator/k8-operator/config/rbac/role.yaml create mode 100644 k8-operator/k8-operator/config/rbac/role_binding.yaml create mode 100644 k8-operator/k8-operator/config/rbac/service_account.yaml create mode 100644 k8-operator/k8-operator/config/samples/kustomization.yaml create mode 100644 k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicaldynamicsecret.yaml create mode 100644 k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalpushsecretsecret.yaml create mode 100644 k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalsecret.yaml create mode 100644 k8-operator/k8-operator/go.mod create mode 100644 k8-operator/k8-operator/go.sum create mode 100644 k8-operator/k8-operator/hack/boilerplate.go.txt create mode 100644 k8-operator/k8-operator/internal/api/api.go create mode 100644 k8-operator/k8-operator/internal/api/models.go create mode 100644 k8-operator/k8-operator/internal/api/variables.go create mode 100644 k8-operator/k8-operator/internal/constants/constants.go create mode 100644 k8-operator/k8-operator/internal/controller/infisicaldynamicsecret_controller.go create mode 100644 k8-operator/k8-operator/internal/controller/infisicaldynamicsecret_controller_test.go create mode 100644 k8-operator/k8-operator/internal/controller/infisicalpushsecretsecret_controller.go create mode 100644 k8-operator/k8-operator/internal/controller/infisicalpushsecretsecret_controller_test.go create mode 100644 k8-operator/k8-operator/internal/controller/infisicalsecret_controller.go create mode 100644 k8-operator/k8-operator/internal/controller/infisicalsecret_controller_test.go create mode 100644 k8-operator/k8-operator/internal/controller/suite_test.go create mode 100644 k8-operator/k8-operator/internal/controllerhelpers/controllerhelpers.go create mode 100644 k8-operator/k8-operator/internal/controllerutil/util.go create mode 100644 k8-operator/k8-operator/internal/crypto/crypto.go create mode 100644 k8-operator/k8-operator/internal/generator/generator.go create mode 100644 k8-operator/k8-operator/internal/generator/password.go create mode 100644 k8-operator/k8-operator/internal/generator/uuid.go create mode 100644 k8-operator/k8-operator/internal/model/model.go create mode 100644 k8-operator/k8-operator/internal/services/infisicalsecret/conditions.go create mode 100644 k8-operator/k8-operator/internal/services/infisicalsecret/handler.go create mode 100644 k8-operator/k8-operator/internal/services/infisicalsecret/reconciler.go create mode 100644 k8-operator/k8-operator/internal/services/infisicalsecret/suite_test.go create mode 100644 k8-operator/k8-operator/internal/template/base64.go create mode 100644 k8-operator/k8-operator/internal/template/jwk.go create mode 100644 k8-operator/k8-operator/internal/template/pem.go create mode 100644 k8-operator/k8-operator/internal/template/pem_chain.go create mode 100644 k8-operator/k8-operator/internal/template/pkcs12.go create mode 100644 k8-operator/k8-operator/internal/template/template.go create mode 100644 k8-operator/k8-operator/internal/template/yaml.go create mode 100644 k8-operator/k8-operator/internal/util/auth.go create mode 100644 k8-operator/k8-operator/internal/util/helpers.go create mode 100644 k8-operator/k8-operator/internal/util/kubernetes.go create mode 100644 k8-operator/k8-operator/internal/util/models.go create mode 100644 k8-operator/k8-operator/internal/util/secrets.go create mode 100644 k8-operator/k8-operator/internal/util/time.go create mode 100644 k8-operator/k8-operator/internal/util/workspace.go create mode 100644 k8-operator/k8-operator/test/e2e/e2e_suite_test.go create mode 100644 k8-operator/k8-operator/test/e2e/e2e_test.go create mode 100644 k8-operator/k8-operator/test/utils/utils.go diff --git a/k8-operator/k8-operator/.devcontainer/devcontainer.json b/k8-operator/k8-operator/.devcontainer/devcontainer.json new file mode 100644 index 000000000..a3ab7541c --- /dev/null +++ b/k8-operator/k8-operator/.devcontainer/devcontainer.json @@ -0,0 +1,25 @@ +{ + "name": "Kubebuilder DevContainer", + "image": "golang:1.24", + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": {}, + "ghcr.io/devcontainers/features/git:1": {} + }, + + "runArgs": ["--network=host"], + + "customizations": { + "vscode": { + "settings": { + "terminal.integrated.shell.linux": "/bin/bash" + }, + "extensions": [ + "ms-kubernetes-tools.vscode-kubernetes-tools", + "ms-azuretools.vscode-docker" + ] + } + }, + + "onCreateCommand": "bash .devcontainer/post-install.sh" +} + diff --git a/k8-operator/k8-operator/.devcontainer/post-install.sh b/k8-operator/k8-operator/.devcontainer/post-install.sh new file mode 100644 index 000000000..265c43ee8 --- /dev/null +++ b/k8-operator/k8-operator/.devcontainer/post-install.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -x + +curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 +chmod +x ./kind +mv ./kind /usr/local/bin/kind + +curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/linux/amd64 +chmod +x kubebuilder +mv kubebuilder /usr/local/bin/ + +KUBECTL_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt) +curl -LO "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/amd64/kubectl" +chmod +x kubectl +mv kubectl /usr/local/bin/kubectl + +docker network create -d=bridge --subnet=172.19.0.0/24 kind + +kind version +kubebuilder version +docker --version +go version +kubectl version --client diff --git a/k8-operator/k8-operator/.dockerignore b/k8-operator/k8-operator/.dockerignore new file mode 100644 index 000000000..a3aab7af7 --- /dev/null +++ b/k8-operator/k8-operator/.dockerignore @@ -0,0 +1,3 @@ +# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file +# Ignore build and test binaries. +bin/ diff --git a/k8-operator/k8-operator/.github/workflows/lint.yml b/k8-operator/k8-operator/.github/workflows/lint.yml new file mode 100644 index 000000000..67ff2bf09 --- /dev/null +++ b/k8-operator/k8-operator/.github/workflows/lint.yml @@ -0,0 +1,23 @@ +name: Lint + +on: + push: + pull_request: + +jobs: + lint: + name: Run on Ubuntu + runs-on: ubuntu-latest + steps: + - name: Clone the code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Run linter + uses: golangci/golangci-lint-action@v8 + with: + version: v2.1.6 diff --git a/k8-operator/k8-operator/.github/workflows/test-e2e.yml b/k8-operator/k8-operator/.github/workflows/test-e2e.yml new file mode 100644 index 000000000..68fd1ed55 --- /dev/null +++ b/k8-operator/k8-operator/.github/workflows/test-e2e.yml @@ -0,0 +1,32 @@ +name: E2E Tests + +on: + push: + pull_request: + +jobs: + test-e2e: + name: Run on Ubuntu + runs-on: ubuntu-latest + steps: + - name: Clone the code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Install the latest version of kind + run: | + curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 + chmod +x ./kind + sudo mv ./kind /usr/local/bin/kind + + - name: Verify kind installation + run: kind version + + - name: Running Test e2e + run: | + go mod tidy + make test-e2e diff --git a/k8-operator/k8-operator/.github/workflows/test.yml b/k8-operator/k8-operator/.github/workflows/test.yml new file mode 100644 index 000000000..fc2e80d30 --- /dev/null +++ b/k8-operator/k8-operator/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: Tests + +on: + push: + pull_request: + +jobs: + test: + name: Run on Ubuntu + runs-on: ubuntu-latest + steps: + - name: Clone the code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Running Tests + run: | + go mod tidy + make test diff --git a/k8-operator/k8-operator/.gitignore b/k8-operator/k8-operator/.gitignore new file mode 100644 index 000000000..ada68ff08 --- /dev/null +++ b/k8-operator/k8-operator/.gitignore @@ -0,0 +1,27 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib +bin/* +Dockerfile.cross + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Go workspace file +go.work + +# Kubernetes Generated files - skip generated files, except for vendored files +!vendor/**/zz_generated.* + +# editor and IDE paraphernalia +.idea +.vscode +*.swp +*.swo +*~ diff --git a/k8-operator/k8-operator/.golangci.yml b/k8-operator/k8-operator/.golangci.yml new file mode 100644 index 000000000..e5b21b0f1 --- /dev/null +++ b/k8-operator/k8-operator/.golangci.yml @@ -0,0 +1,52 @@ +version: "2" +run: + allow-parallel-runners: true +linters: + default: none + enable: + - copyloopvar + - dupl + - errcheck + - ginkgolinter + - goconst + - gocyclo + - govet + - ineffassign + - lll + - misspell + - nakedret + - prealloc + - revive + - staticcheck + - unconvert + - unparam + - unused + settings: + revive: + rules: + - name: comment-spacings + - name: import-shadowing + exclusions: + generated: lax + rules: + - linters: + - lll + path: api/* + - linters: + - dupl + - lll + path: internal/* + paths: + - third_party$ + - builtin$ + - examples$ +formatters: + enable: + - gofmt + - goimports + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/k8-operator/k8-operator/Dockerfile b/k8-operator/k8-operator/Dockerfile new file mode 100644 index 000000000..cb1b130fd --- /dev/null +++ b/k8-operator/k8-operator/Dockerfile @@ -0,0 +1,33 @@ +# Build the manager binary +FROM golang:1.24 AS builder +ARG TARGETOS +ARG TARGETARCH + +WORKDIR /workspace +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum +# 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 the go source +COPY cmd/main.go cmd/main.go +COPY api/ api/ +COPY internal/ internal/ + +# Build +# the GOARCH has not a default value to allow the binary be built according to the host where the command +# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO +# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, +# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go + +# Use distroless as minimal base image to package the manager binary +# Refer to https://github.com/GoogleContainerTools/distroless for more details +FROM gcr.io/distroless/static:nonroot +WORKDIR / +COPY --from=builder /workspace/manager . +USER 65532:65532 + +ENTRYPOINT ["/manager"] diff --git a/k8-operator/k8-operator/Makefile b/k8-operator/k8-operator/Makefile new file mode 100644 index 000000000..b776cf7a9 --- /dev/null +++ b/k8-operator/k8-operator/Makefile @@ -0,0 +1,238 @@ +# Image URL to use all building/pushing image targets +IMG ?= controller: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 + +# CONTAINER_TOOL defines the container tool to be used for building images. +# Be aware that the target commands are only tested with Docker which is +# scaffolded by default. However, you might want to replace it to use other +# tools. (i.e. podman) +CONTAINER_TOOL ?= docker + +# Setting SHELL to bash allows bash commands to be executed by recipes. +# 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: build + +##@ 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 command 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: help +help: ## Display this help. + @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) + +##@ Development + +.PHONY: manifests +manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. + $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases + +.PHONY: generate +generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. + $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." + +.PHONY: fmt +fmt: ## Run go fmt against code. + go fmt ./... + +.PHONY: vet +vet: ## Run go vet against code. + go vet ./... + +.PHONY: test +test: manifests generate fmt vet setup-envtest ## Run tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out + +# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. +# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. +# CertManager is installed by default; skip with: +# - CERT_MANAGER_INSTALL_SKIP=true +KIND_CLUSTER ?= k8-operator-test-e2e + +.PHONY: setup-test-e2e +setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist + @command -v $(KIND) >/dev/null 2>&1 || { \ + echo "Kind is not installed. Please install Kind manually."; \ + exit 1; \ + } + @case "$$($(KIND) get clusters)" in \ + *"$(KIND_CLUSTER)"*) \ + echo "Kind cluster '$(KIND_CLUSTER)' already exists. Skipping creation." ;; \ + *) \ + echo "Creating Kind cluster '$(KIND_CLUSTER)'..."; \ + $(KIND) create cluster --name $(KIND_CLUSTER) ;; \ + esac + +.PHONY: test-e2e +test-e2e: setup-test-e2e manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind. + KIND_CLUSTER=$(KIND_CLUSTER) go test ./test/e2e/ -v -ginkgo.v + $(MAKE) cleanup-test-e2e + +.PHONY: cleanup-test-e2e +cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests + @$(KIND) delete cluster --name $(KIND_CLUSTER) + +.PHONY: lint +lint: golangci-lint ## Run golangci-lint linter + $(GOLANGCI_LINT) run + +.PHONY: lint-fix +lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes + $(GOLANGCI_LINT) run --fix + +.PHONY: lint-config +lint-config: golangci-lint ## Verify golangci-lint linter configuration + $(GOLANGCI_LINT) config verify + +##@ Build + +.PHONY: build +build: manifests generate fmt vet ## Build manager binary. + go build -o bin/manager cmd/main.go + +.PHONY: run +run: manifests generate fmt vet ## Run a controller from your host. + go run ./cmd/main.go + +# If you wish to build the manager image targeting other platforms you can use the --platform flag. +# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. +# More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +.PHONY: docker-build +docker-build: ## Build docker image with the manager. + $(CONTAINER_TOOL) build -t ${IMG} . + +.PHONY: docker-push +docker-push: ## Push docker image with the manager. + $(CONTAINER_TOOL) push ${IMG} + +# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple +# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: +# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/ +# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=> then the export will fail) +# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option. +PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le +.PHONY: docker-buildx +docker-buildx: ## Build and push docker image for the manager for cross-platform support + # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile + sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross + - $(CONTAINER_TOOL) buildx create --name k8-operator-builder + $(CONTAINER_TOOL) buildx use k8-operator-builder + - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . + - $(CONTAINER_TOOL) buildx rm k8-operator-builder + rm Dockerfile.cross + +.PHONY: build-installer +build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment. + mkdir -p dist + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default > dist/install.yaml + +##@ Deployment + +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: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - + +.PHONY: undeploy +undeploy: kustomize ## 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 - + +##@ Dependencies + +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) + +## Tool Binaries +KUBECTL ?= kubectl +KIND ?= kind +KUSTOMIZE ?= $(LOCALBIN)/kustomize +CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen +ENVTEST ?= $(LOCALBIN)/setup-envtest +GOLANGCI_LINT = $(LOCALBIN)/golangci-lint + +## Tool Versions +KUSTOMIZE_VERSION ?= v5.6.0 +CONTROLLER_TOOLS_VERSION ?= v0.18.0 +#ENVTEST_VERSION is the version of controller-runtime release branch to fetch the envtest setup script (i.e. release-0.20) +ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}') +#ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31) +ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d", $$3}') +GOLANGCI_LINT_VERSION ?= v2.1.6 + +.PHONY: kustomize +kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. +$(KUSTOMIZE): $(LOCALBIN) + $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) + +.PHONY: controller-gen +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. +$(CONTROLLER_GEN): $(LOCALBIN) + $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) + +.PHONY: setup-envtest +setup-envtest: envtest ## Download the binaries required for ENVTEST in the local bin directory. + @echo "Setting up envtest binaries for Kubernetes version $(ENVTEST_K8S_VERSION)..." + @$(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path || { \ + echo "Error: Failed to set up envtest binaries for version $(ENVTEST_K8S_VERSION)."; \ + exit 1; \ + } + +.PHONY: envtest +envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. +$(ENVTEST): $(LOCALBIN) + $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION)) + +.PHONY: golangci-lint +golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. +$(GOLANGCI_LINT): $(LOCALBIN) + $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) + +# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist +# $1 - target path with name of binary +# $2 - package url which can be installed +# $3 - specific version of package +define go-install-tool +@[ -f "$(1)-$(3)" ] || { \ +set -e; \ +package=$(2)@$(3) ;\ +echo "Downloading $${package}" ;\ +rm -f $(1) || true ;\ +GOBIN=$(LOCALBIN) go install $${package} ;\ +mv $(1) $(1)-$(3) ;\ +} ;\ +ln -sf $(1)-$(3) $(1) +endef diff --git a/k8-operator/k8-operator/PROJECT b/k8-operator/k8-operator/PROJECT new file mode 100644 index 000000000..dc9260f24 --- /dev/null +++ b/k8-operator/k8-operator/PROJECT @@ -0,0 +1,39 @@ +# 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 +cliVersion: 4.7.0 +domain: infisical.com +layout: +- go.kubebuilder.io/v4 +projectName: k8-operator +repo: github.com/Infisical/infisical/k8-operator +resources: +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: infisical.com + group: secrets + kind: InfisicalSecret + path: github.com/Infisical/infisical/k8-operator/api/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: infisical.com + group: secrets + kind: InfisicalPushSecretSecret + path: github.com/Infisical/infisical/k8-operator/api/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: infisical.com + group: secrets + kind: InfisicalDynamicSecret + path: github.com/Infisical/infisical/k8-operator/api/v1alpha1 + version: v1alpha1 +version: "3" diff --git a/k8-operator/k8-operator/README.md b/k8-operator/k8-operator/README.md new file mode 100644 index 000000000..e5cab2089 --- /dev/null +++ b/k8-operator/k8-operator/README.md @@ -0,0 +1,135 @@ +# k8-operator +// TODO(user): Add simple overview of use/purpose + +## Description +// TODO(user): An in-depth paragraph about your project and overview of use + +## Getting Started + +### Prerequisites +- go version v1.24.0+ +- docker version 17.03+. +- kubectl version v1.11.3+. +- Access to a Kubernetes v1.11.3+ cluster. + +### To Deploy on the cluster +**Build and push your image to the location specified by `IMG`:** + +```sh +make docker-build docker-push IMG=/k8-operator:tag +``` + +**NOTE:** This image ought to be published in the personal registry you specified. +And it is required to have access to pull the image from the working environment. +Make sure you have the proper permission to the registry if the above commands don’t work. + +**Install the CRDs into the cluster:** + +```sh +make install +``` + +**Deploy the Manager to the cluster with the image specified by `IMG`:** + +```sh +make deploy IMG=/k8-operator:tag +``` + +> **NOTE**: If you encounter RBAC errors, you may need to grant yourself cluster-admin +privileges or be logged in as admin. + +**Create instances of your solution** +You can apply the samples (examples) from the config/sample: + +```sh +kubectl apply -k config/samples/ +``` + +>**NOTE**: Ensure that the samples has default values to test it out. + +### To Uninstall +**Delete the instances (CRs) from the cluster:** + +```sh +kubectl delete -k config/samples/ +``` + +**Delete the APIs(CRDs) from the cluster:** + +```sh +make uninstall +``` + +**UnDeploy the controller from the cluster:** + +```sh +make undeploy +``` + +## Project Distribution + +Following the options to release and provide this solution to the users. + +### By providing a bundle with all YAML files + +1. Build the installer for the image built and published in the registry: + +```sh +make build-installer IMG=/k8-operator:tag +``` + +**NOTE:** The makefile target mentioned above generates an 'install.yaml' +file in the dist directory. This file contains all the resources built +with Kustomize, which are necessary to install this project without its +dependencies. + +2. Using the installer + +Users can just run 'kubectl apply -f ' to install +the project, i.e.: + +```sh +kubectl apply -f https://raw.githubusercontent.com//k8-operator//dist/install.yaml +``` + +### By providing a Helm Chart + +1. Build the chart using the optional helm plugin + +```sh +kubebuilder edit --plugins=helm/v1-alpha +``` + +2. See that a chart was generated under 'dist/chart', and users +can obtain this solution from there. + +**NOTE:** If you change the project, you need to update the Helm Chart +using the same command above to sync the latest changes. Furthermore, +if you create webhooks, you need to use the above command with +the '--force' flag and manually ensure that any custom configuration +previously added to 'dist/chart/values.yaml' or 'dist/chart/manager/manager.yaml' +is manually re-applied afterwards. + +## Contributing +// TODO(user): Add detailed information on how you would like others to contribute to this project + +**NOTE:** Run `make help` for more information on all potential `make` targets + +More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html) + +## License + +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + diff --git a/k8-operator/k8-operator/api/v1alpha1/common.go b/k8-operator/k8-operator/api/v1alpha1/common.go new file mode 100644 index 000000000..2362857d8 --- /dev/null +++ b/k8-operator/k8-operator/api/v1alpha1/common.go @@ -0,0 +1,149 @@ +package v1alpha1 + +type GenericInfisicalAuthentication struct { + // +kubebuilder:validation:Optional + UniversalAuth GenericUniversalAuth `json:"universalAuth,omitempty"` + // +kubebuilder:validation:Optional + KubernetesAuth GenericKubernetesAuth `json:"kubernetesAuth,omitempty"` + // +kubebuilder:validation:Optional + AwsIamAuth GenericAwsIamAuth `json:"awsIamAuth,omitempty"` + // +kubebuilder:validation:Optional + AzureAuth GenericAzureAuth `json:"azureAuth,omitempty"` + // +kubebuilder:validation:Optional + GcpIdTokenAuth GenericGcpIdTokenAuth `json:"gcpIdTokenAuth,omitempty"` + // +kubebuilder:validation:Optional + GcpIamAuth GenericGcpIamAuth `json:"gcpIamAuth,omitempty"` +} + +type GenericUniversalAuth struct { + // +kubebuilder:validation:Required + CredentialsRef KubeSecretReference `json:"credentialsRef"` +} + +type GenericAwsIamAuth struct { + // +kubebuilder:validation:Required + IdentityID string `json:"identityId"` +} + +type GenericAzureAuth struct { + // +kubebuilder:validation:Required + IdentityID string `json:"identityId"` + // +kubebuilder:validation:Optional + Resource string `json:"resource,omitempty"` +} + +type GenericGcpIdTokenAuth struct { + // +kubebuilder:validation:Required + IdentityID string `json:"identityId"` +} + +type GenericGcpIamAuth struct { + // +kubebuilder:validation:Required + IdentityID string `json:"identityId"` + // +kubebuilder:validation:Required + ServiceAccountKeyFilePath string `json:"serviceAccountKeyFilePath"` +} + +type GenericKubernetesAuth struct { + // +kubebuilder:validation:Required + IdentityID string `json:"identityId"` + // +kubebuilder:validation:Required + ServiceAccountRef KubernetesServiceAccountRef `json:"serviceAccountRef"` + + // Optionally automatically create a service account token for the configured service account. + // If this is set to `true`, the operator will automatically create a service account token for the configured service account. This field is recommended in most cases. + // +kubebuilder:validation:Optional + AutoCreateServiceAccountToken bool `json:"autoCreateServiceAccountToken"` + // The audiences to use for the service account token. This is only relevant if `autoCreateServiceAccountToken` is true. + // +kubebuilder:validation:Optional + ServiceAccountTokenAudiences []string `json:"serviceAccountTokenAudiences"` +} + +type TLSConfig struct { + // Reference to secret containing CA cert + // +kubebuilder:validation:Optional + CaRef CaReference `json:"caRef,omitempty"` +} + +type CaReference struct { + // The name of the Kubernetes Secret + // +kubebuilder:validation:Required + SecretName string `json:"secretName"` + + // The namespace where the Kubernetes Secret is located + // +kubebuilder:validation:Required + SecretNamespace string `json:"secretNamespace"` + + // +kubebuilder:validation:Required + // The name of the secret property with the CA certificate value + SecretKey string `json:"key"` +} + +type KubeSecretReference struct { + // The name of the Kubernetes Secret + // +kubebuilder:validation:Required + SecretName string `json:"secretName"` + + // The name space where the Kubernetes Secret is located + // +kubebuilder:validation:Required + SecretNamespace string `json:"secretNamespace"` +} + +type ManagedKubeSecretConfig struct { + // The name of the Kubernetes Secret + // +kubebuilder:validation:Required + SecretName string `json:"secretName"` + + // The name space where the Kubernetes Secret is located + // +kubebuilder:validation:Required + SecretNamespace string `json:"secretNamespace"` + + // The Kubernetes Secret type (experimental feature). More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types + // +kubebuilder:validation:Optional + // +kubebuilder:default:=Opaque + SecretType string `json:"secretType"` + + // The Kubernetes Secret creation policy. + // Enum with values: 'Owner', 'Orphan'. + // Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. + // Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted. + // +kubebuilder:validation:Optional + // +kubebuilder:default:=Orphan + CreationPolicy string `json:"creationPolicy"` + + // The template to transform the secret data + // +kubebuilder:validation:Optional + Template *SecretTemplate `json:"template,omitempty"` +} + +type ManagedKubeConfigMapConfig struct { + // The name of the Kubernetes ConfigMap + // +kubebuilder:validation:Required + ConfigMapName string `json:"configMapName"` + + // The Kubernetes ConfigMap creation policy. + // Enum with values: 'Owner', 'Orphan'. + // Owner creates the config map and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. + // Orphan will not set the config map owner. This will result in the config map being orphaned and not deleted when the resource is deleted. + // +kubebuilder:validation:Optional + // +kubebuilder:default:=Orphan + CreationPolicy string `json:"creationPolicy"` + + // The namespace where the Kubernetes ConfigMap is located + // +kubebuilder:validation:Required + ConfigMapNamespace string `json:"configMapNamespace"` + + // The template to transform the secret data + // +kubebuilder:validation:Optional + Template *SecretTemplate `json:"template,omitempty"` +} + +type SecretTemplate struct { + // This injects all retrieved secrets into the top level of your template. + // Secrets defined in the template will take precedence over the injected ones. + // +kubebuilder:validation:Optional + IncludeAllSecrets bool `json:"includeAllSecrets"` + // The template key values + // +kubebuilder:validation:Optional + Data map[string]string `json:"data,omitempty"` +} diff --git a/k8-operator/k8-operator/api/v1alpha1/generators.go b/k8-operator/k8-operator/api/v1alpha1/generators.go new file mode 100644 index 000000000..0f6d86c2d --- /dev/null +++ b/k8-operator/k8-operator/api/v1alpha1/generators.go @@ -0,0 +1,152 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// GeneratorKind represents a kind of generator. +// +kubebuilder:validation:Enum=Password;UUID +type GeneratorKind string + +const ( + GeneratorKindPassword GeneratorKind = "Password" + GeneratorKindUUID GeneratorKind = "UUID" +) + +type ClusterGeneratorSpec struct { + // Kind the kind of this generator. + Kind GeneratorKind `json:"kind"` + + // Generator the spec for this generator, must match the kind. + Generator GeneratorSpec `json:"generator,omitempty"` +} + +type GeneratorSpec struct { + // +kubebuilder:validation:Optional + PasswordSpec *PasswordSpec `json:"passwordSpec,omitempty"` + // +kubebuilder:validation:Optional + UUIDSpec *UUIDSpec `json:"uuidSpec,omitempty"` +} + +// ClusterGenerator represents a cluster-wide generator +// +kubebuilder:object:root=true +// +kubebuilder:storageversion +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster +type ClusterGenerator struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ClusterGeneratorSpec `json:"spec,omitempty"` +} + +// +kubebuilder:object:root=true + +// ClusterGeneratorList contains a list of ClusterGenerator resources. +type ClusterGeneratorList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ClusterGenerator `json:"items"` +} + +// ! UUID Generator + +// UUIDSpec controls the behavior of the uuid generator. +type UUIDSpec struct{} + +// UUID generates a version 4 UUID (e56657e3-764f-11ef-a397-65231a88c216). +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +type UUID struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec UUIDSpec `json:"spec,omitempty"` +} + +// +kubebuilder:object:root=true + +// UUIDList contains a list of UUID resources. +type UUIDList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []UUID `json:"items"` +} + +// ! Password Generator + +// PasswordSpec controls the behavior of the password generator. +type PasswordSpec struct { + // Length of the password to be generated. + // Defaults to 24 + // +kubebuilder:validation:Optional + // +kubebuilder:default=24 + Length int `json:"length"` + + // digits specifies the number of digits in the generated + // password. If omitted it defaults to 25% of the length of the password + Digits *int `json:"digits,omitempty"` + + // symbols specifies the number of symbol characters in the generated + // password. If omitted it defaults to 25% of the length of the password + Symbols *int `json:"symbols,omitempty"` + + // symbolCharacters specifies the special characters that should be used + // in the generated password. + SymbolCharacters *string `json:"symbolCharacters,omitempty"` + + // Set noUpper to disable uppercase characters + // +kubebuilder:validation:Optional + // +kubebuilder:default=false + NoUpper bool `json:"noUpper"` + + // set allowRepeat to true to allow repeating characters. + // +kubebuilder:validation:Optional + // +kubebuilder:default=false + AllowRepeat bool `json:"allowRepeat"` +} + +// Password generates a random password based on the +// configuration parameters in spec. +// You can specify the length, characterset and other attributes. +// +kubebuilder:object:root=true +// +kubebuilder:storageversion +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Namespaced +type Password struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec PasswordSpec `json:"spec,omitempty"` +} + +// +kubebuilder:object:root=true + +// PasswordList contains a list of Password resources. +type PasswordList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Password `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Password{}, &PasswordList{}) + SchemeBuilder.Register(&UUID{}, &UUIDList{}) + SchemeBuilder.Register(&ClusterGenerator{}, &ClusterGeneratorList{}) +} diff --git a/k8-operator/k8-operator/api/v1alpha1/groupversion_info.go b/k8-operator/k8-operator/api/v1alpha1/groupversion_info.go new file mode 100644 index 000000000..36ebd80ce --- /dev/null +++ b/k8-operator/k8-operator/api/v1alpha1/groupversion_info.go @@ -0,0 +1,20 @@ +// Package v1alpha1 contains API Schema definitions for the secrets v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=secrets.infisical.com +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "secrets.infisical.com", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/k8-operator/k8-operator/api/v1alpha1/infisicaldynamicsecret_types.go b/k8-operator/k8-operator/api/v1alpha1/infisicaldynamicsecret_types.go new file mode 100644 index 000000000..a55e215a3 --- /dev/null +++ b/k8-operator/k8-operator/api/v1alpha1/infisicaldynamicsecret_types.go @@ -0,0 +1,99 @@ +/* +Copyright 2022. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type InfisicalDynamicSecretLease struct { + ID string `json:"id"` + Version int64 `json:"version"` + CreationTimestamp metav1.Time `json:"creationTimestamp"` + ExpiresAt metav1.Time `json:"expiresAt"` +} + +type DynamicSecretDetails struct { + // +kubebuilder:validation:Required + // +kubebuilder:validation:Immutable + SecretName string `json:"secretName"` + // +kubebuilder:validation:Required + // +kubebuilder:validation:Immutable + SecretPath string `json:"secretsPath"` + // +kubebuilder:validation:Required + // +kubebuilder:validation:Immutable + EnvironmentSlug string `json:"environmentSlug"` + // +kubebuilder:validation:Required + // +kubebuilder:validation:Immutable + ProjectID string `json:"projectId"` +} + +// InfisicalDynamicSecretSpec defines the desired state of InfisicalDynamicSecret. +type InfisicalDynamicSecretSpec struct { + // +kubebuilder:validation:Required + ManagedSecretReference ManagedKubeSecretConfig `json:"managedSecretReference"` // The destination to store the lease in. + + // +kubebuilder:validation:Required + Authentication GenericInfisicalAuthentication `json:"authentication"` // The authentication to use for authenticating with Infisical. + + // +kubebuilder:validation:Required + DynamicSecret DynamicSecretDetails `json:"dynamicSecret"` // The dynamic secret to create the lease for. Required. + + LeaseRevocationPolicy string `json:"leaseRevocationPolicy"` // Revoke will revoke the lease when the resource is deleted. Optional, will default to no revocation. + LeaseTTL string `json:"leaseTTL"` // The TTL of the lease in seconds. Optional, will default to the dynamic secret default TTL. + + // +kubebuilder:validation:Optional + HostAPI string `json:"hostAPI"` + + // +kubebuilder:validation:Optional + TLS TLSConfig `json:"tls"` +} + +// InfisicalDynamicSecretStatus defines the observed state of InfisicalDynamicSecret. +type InfisicalDynamicSecretStatus struct { + Conditions []metav1.Condition `json:"conditions"` + + Lease *InfisicalDynamicSecretLease `json:"lease,omitempty"` + DynamicSecretID string `json:"dynamicSecretId,omitempty"` + // The MaxTTL can be null, if it's null, there's no max TTL and we should never have to renew. + MaxTTL string `json:"maxTTL,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status + +// InfisicalDynamicSecret is the Schema for the infisicaldynamicsecrets API. +type InfisicalDynamicSecret struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec InfisicalDynamicSecretSpec `json:"spec,omitempty"` + Status InfisicalDynamicSecretStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// InfisicalDynamicSecretList contains a list of InfisicalDynamicSecret. +type InfisicalDynamicSecretList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []InfisicalDynamicSecret `json:"items"` +} + +func init() { + SchemeBuilder.Register(&InfisicalDynamicSecret{}, &InfisicalDynamicSecretList{}) +} diff --git a/k8-operator/k8-operator/api/v1alpha1/infisicalpushsecret_types.go b/k8-operator/k8-operator/api/v1alpha1/infisicalpushsecret_types.go new file mode 100644 index 000000000..8958c714d --- /dev/null +++ b/k8-operator/k8-operator/api/v1alpha1/infisicalpushsecret_types.go @@ -0,0 +1,115 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type InfisicalPushSecretDestination struct { + // +kubebuilder:validation:Required + // +kubebuilder:validation:Immutable + SecretsPath string `json:"secretsPath"` + // +kubebuilder:validation:Required + // +kubebuilder:validation:Immutable + EnvironmentSlug string `json:"environmentSlug"` + // +kubebuilder:validation:Required + // +kubebuilder:validation:Immutable + ProjectID string `json:"projectId"` +} + +type InfisicalPushSecretSecretSource struct { + // The name of the Kubernetes Secret + // +kubebuilder:validation:Required + SecretName string `json:"secretName"` + + // The name space where the Kubernetes Secret is located + // +kubebuilder:validation:Required + SecretNamespace string `json:"secretNamespace"` + + // +kubebuilder:validation:Optional + Template *SecretTemplate `json:"template,omitempty"` +} + +type GeneratorRef struct { + // Specify the Kind of the generator resource + // +kubebuilder:validation:Enum=Password;UUID + // +kubebuilder:validation:Required + Kind GeneratorKind `json:"kind"` + + // +kubebuilder:validation:Required + Name string `json:"name"` +} + +type SecretPushGenerator struct { + // +kubebuilder:validation:Required + DestinationSecretName string `json:"destinationSecretName"` + // +kubebuilder:validation:Required + GeneratorRef GeneratorRef `json:"generatorRef"` +} + +type SecretPush struct { + // +kubebuilder:validation:Optional + Secret *InfisicalPushSecretSecretSource `json:"secret,omitempty"` + // +kubebuilder:validation:Optional + Generators []SecretPushGenerator `json:"generators,omitempty"` +} + +// InfisicalPushSecretSpec defines the desired state of InfisicalPushSecret +type InfisicalPushSecretSpec struct { + // +kubebuilder:validation:Optional + UpdatePolicy string `json:"updatePolicy"` + + // +kubebuilder:validation:Optional + DeletionPolicy string `json:"deletionPolicy"` + + // +kubebuilder:validation:Required + // +kubebuilder:validation:Immutable + Destination InfisicalPushSecretDestination `json:"destination"` + + // +kubebuilder:validation:Optional + Authentication GenericInfisicalAuthentication `json:"authentication"` + + // +kubebuilder:validation:Required + Push SecretPush `json:"push"` + + // +kubebuilder:validation:Optional + ResyncInterval *string `json:"resyncInterval,omitempty"` + + // Infisical host to pull secrets from + // +kubebuilder:validation:Optional + HostAPI string `json:"hostAPI"` + + // +kubebuilder:validation:Optional + TLS TLSConfig `json:"tls"` +} + +// InfisicalPushSecretStatus defines the observed state of InfisicalPushSecret +type InfisicalPushSecretStatus struct { + Conditions []metav1.Condition `json:"conditions"` + + // managed secrets is a map where the key is the ID, and the value is the secret key (string[id], string[key] ) + ManagedSecrets map[string]string `json:"managedSecrets"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// InfisicalPushSecret is the Schema for the infisicalpushsecrets API +type InfisicalPushSecret struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec InfisicalPushSecretSpec `json:"spec,omitempty"` + Status InfisicalPushSecretStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// InfisicalPushSecretList contains a list of InfisicalPushSecret +type InfisicalPushSecretList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []InfisicalPushSecret `json:"items"` +} + +func init() { + SchemeBuilder.Register(&InfisicalPushSecret{}, &InfisicalPushSecretList{}) +} diff --git a/k8-operator/k8-operator/api/v1alpha1/infisicalsecret_types.go b/k8-operator/k8-operator/api/v1alpha1/infisicalsecret_types.go new file mode 100644 index 000000000..ff26a878c --- /dev/null +++ b/k8-operator/k8-operator/api/v1alpha1/infisicalsecret_types.go @@ -0,0 +1,182 @@ +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type Authentication struct { + // +kubebuilder:validation:Optional + ServiceAccount ServiceAccountDetails `json:"serviceAccount"` + // +kubebuilder:validation:Optional + ServiceToken ServiceTokenDetails `json:"serviceToken"` + // +kubebuilder:validation:Optional + UniversalAuth UniversalAuthDetails `json:"universalAuth"` + // +kubebuilder:validation:Optional + KubernetesAuth KubernetesAuthDetails `json:"kubernetesAuth"` + // +kubebuilder:validation:Optional + AwsIamAuth AWSIamAuthDetails `json:"awsIamAuth"` + // +kubebuilder:validation:Optional + AzureAuth AzureAuthDetails `json:"azureAuth"` + // +kubebuilder:validation:Optional + GcpIdTokenAuth GCPIdTokenAuthDetails `json:"gcpIdTokenAuth"` + // +kubebuilder:validation:Optional + GcpIamAuth GcpIamAuthDetails `json:"gcpIamAuth"` +} + +type UniversalAuthDetails struct { + // +kubebuilder:validation:Required + CredentialsRef KubeSecretReference `json:"credentialsRef"` + // +kubebuilder:validation:Required + SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"` +} + +type KubernetesAuthDetails struct { + // +kubebuilder:validation:Required + IdentityID string `json:"identityId"` + // +kubebuilder:validation:Required + ServiceAccountRef KubernetesServiceAccountRef `json:"serviceAccountRef"` + + // +kubebuilder:validation:Required + SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"` + + // Optionally automatically create a service account token for the configured service account. + // If this is set to `true`, the operator will automatically create a service account token for the configured service account. + // +kubebuilder:validation:Optional + AutoCreateServiceAccountToken bool `json:"autoCreateServiceAccountToken"` + // The audiences to use for the service account token. This is only relevant if `autoCreateServiceAccountToken` is true. + // +kubebuilder:validation:Optional + ServiceAccountTokenAudiences []string `json:"serviceAccountTokenAudiences"` +} + +type KubernetesServiceAccountRef struct { + // +kubebuilder:validation:Required + Name string `json:"name"` + // +kubebuilder:validation:Required + Namespace string `json:"namespace"` +} + +type AWSIamAuthDetails struct { + // +kubebuilder:validation:Required + IdentityID string `json:"identityId"` + + // +kubebuilder:validation:Required + SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"` +} + +type AzureAuthDetails struct { + // +kubebuilder:validation:Required + IdentityID string `json:"identityId"` + // +kubebuilder:validation:Optional + Resource string `json:"resource"` + + // +kubebuilder:validation:Required + SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"` +} + +type GCPIdTokenAuthDetails struct { + // +kubebuilder:validation:Required + IdentityID string `json:"identityId"` + + // +kubebuilder:validation:Required + SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"` +} + +type GcpIamAuthDetails struct { + // +kubebuilder:validation:Required + IdentityID string `json:"identityId"` + // +kubebuilder:validation:Required + ServiceAccountKeyFilePath string `json:"serviceAccountKeyFilePath"` + + // +kubebuilder:validation:Required + SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"` +} + +type ServiceTokenDetails struct { + // +kubebuilder:validation:Required + ServiceTokenSecretReference KubeSecretReference `json:"serviceTokenSecretReference"` + // +kubebuilder:validation:Required + SecretsScope SecretScopeInWorkspace `json:"secretsScope"` +} + +type ServiceAccountDetails struct { + ServiceAccountSecretReference KubeSecretReference `json:"serviceAccountSecretReference"` + ProjectId string `json:"projectId"` + EnvironmentName string `json:"environmentName"` +} + +type SecretScopeInWorkspace struct { + // +kubebuilder:validation:Required + SecretsPath string `json:"secretsPath"` + // +kubebuilder:validation:Required + EnvSlug string `json:"envSlug"` + // +kubebuilder:validation:Optional + Recursive bool `json:"recursive"` +} + +type MachineIdentityScopeInWorkspace struct { + // +kubebuilder:validation:Required + SecretsPath string `json:"secretsPath"` + // +kubebuilder:validation:Required + EnvSlug string `json:"envSlug"` + // +kubebuilder:validation:Required + ProjectSlug string `json:"projectSlug"` + // +kubebuilder:validation:Optional + Recursive bool `json:"recursive"` +} + +// InfisicalSecretSpec defines the desired state of InfisicalSecret +type InfisicalSecretSpec struct { + // +kubebuilder:validation:Optional + TokenSecretReference KubeSecretReference `json:"tokenSecretReference"` + + // +kubebuilder:validation:Optional + Authentication Authentication `json:"authentication"` + + // +kubebuilder:validation:Optional + ManagedSecretReference ManagedKubeSecretConfig `json:"managedSecretReference"` + + // +kubebuilder:validation:Optional + ManagedKubeSecretReferences []ManagedKubeSecretConfig `json:"managedKubeSecretReferences"` + // +kubebuilder:validation:Optional + ManagedKubeConfigMapReferences []ManagedKubeConfigMapConfig `json:"managedKubeConfigMapReferences"` + + // +kubebuilder:default:=60 + ResyncInterval int `json:"resyncInterval"` + + // Infisical host to pull secrets from + // +kubebuilder:validation:Optional + HostAPI string `json:"hostAPI"` + + // +kubebuilder:validation:Optional + TLS TLSConfig `json:"tls"` +} + +// InfisicalSecretStatus defines the observed state of InfisicalSecret +type InfisicalSecretStatus struct { + Conditions []metav1.Condition `json:"conditions"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status + +// InfisicalSecret is the Schema for the infisicalsecrets API +type InfisicalSecret struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec InfisicalSecretSpec `json:"spec,omitempty"` + Status InfisicalSecretStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// InfisicalSecretList contains a list of InfisicalSecret +type InfisicalSecretList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []InfisicalSecret `json:"items"` +} + +func init() { + SchemeBuilder.Register(&InfisicalSecret{}, &InfisicalSecretList{}) +} diff --git a/k8-operator/k8-operator/api/v1alpha1/zz_generated.deepcopy.go b/k8-operator/k8-operator/api/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 000000000..cc4d39c19 --- /dev/null +++ b/k8-operator/k8-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,307 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +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 *InfisicalDynamicSecret) DeepCopyInto(out *InfisicalDynamicSecret) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecret. +func (in *InfisicalDynamicSecret) DeepCopy() *InfisicalDynamicSecret { + if in == nil { + return nil + } + out := new(InfisicalDynamicSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InfisicalDynamicSecret) 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 *InfisicalDynamicSecretList) DeepCopyInto(out *InfisicalDynamicSecretList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]InfisicalDynamicSecret, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecretList. +func (in *InfisicalDynamicSecretList) DeepCopy() *InfisicalDynamicSecretList { + if in == nil { + return nil + } + out := new(InfisicalDynamicSecretList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InfisicalDynamicSecretList) 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 *InfisicalDynamicSecretSpec) DeepCopyInto(out *InfisicalDynamicSecretSpec) { + *out = *in + if in.Foo != nil { + in, out := &in.Foo, &out.Foo + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecretSpec. +func (in *InfisicalDynamicSecretSpec) DeepCopy() *InfisicalDynamicSecretSpec { + if in == nil { + return nil + } + out := new(InfisicalDynamicSecretSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InfisicalDynamicSecretStatus) DeepCopyInto(out *InfisicalDynamicSecretStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecretStatus. +func (in *InfisicalDynamicSecretStatus) DeepCopy() *InfisicalDynamicSecretStatus { + if in == nil { + return nil + } + out := new(InfisicalDynamicSecretStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InfisicalPushSecretSecret) DeepCopyInto(out *InfisicalPushSecretSecret) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalPushSecretSecret. +func (in *InfisicalPushSecretSecret) DeepCopy() *InfisicalPushSecretSecret { + if in == nil { + return nil + } + out := new(InfisicalPushSecretSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InfisicalPushSecretSecret) 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 *InfisicalPushSecretSecretList) DeepCopyInto(out *InfisicalPushSecretSecretList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]InfisicalPushSecretSecret, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalPushSecretSecretList. +func (in *InfisicalPushSecretSecretList) DeepCopy() *InfisicalPushSecretSecretList { + if in == nil { + return nil + } + out := new(InfisicalPushSecretSecretList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InfisicalPushSecretSecretList) 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 *InfisicalPushSecretSecretSpec) DeepCopyInto(out *InfisicalPushSecretSecretSpec) { + *out = *in + if in.Foo != nil { + in, out := &in.Foo, &out.Foo + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalPushSecretSecretSpec. +func (in *InfisicalPushSecretSecretSpec) DeepCopy() *InfisicalPushSecretSecretSpec { + if in == nil { + return nil + } + out := new(InfisicalPushSecretSecretSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InfisicalPushSecretSecretStatus) DeepCopyInto(out *InfisicalPushSecretSecretStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalPushSecretSecretStatus. +func (in *InfisicalPushSecretSecretStatus) DeepCopy() *InfisicalPushSecretSecretStatus { + if in == nil { + return nil + } + out := new(InfisicalPushSecretSecretStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InfisicalSecret) DeepCopyInto(out *InfisicalSecret) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalSecret. +func (in *InfisicalSecret) DeepCopy() *InfisicalSecret { + if in == nil { + return nil + } + out := new(InfisicalSecret) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InfisicalSecret) 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 *InfisicalSecretList) DeepCopyInto(out *InfisicalSecretList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]InfisicalSecret, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalSecretList. +func (in *InfisicalSecretList) DeepCopy() *InfisicalSecretList { + if in == nil { + return nil + } + out := new(InfisicalSecretList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *InfisicalSecretList) 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 *InfisicalSecretSpec) DeepCopyInto(out *InfisicalSecretSpec) { + *out = *in + if in.Foo != nil { + in, out := &in.Foo, &out.Foo + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalSecretSpec. +func (in *InfisicalSecretSpec) DeepCopy() *InfisicalSecretSpec { + if in == nil { + return nil + } + out := new(InfisicalSecretSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *InfisicalSecretStatus) DeepCopyInto(out *InfisicalSecretStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalSecretStatus. +func (in *InfisicalSecretStatus) DeepCopy() *InfisicalSecretStatus { + if in == nil { + return nil + } + out := new(InfisicalSecretStatus) + in.DeepCopyInto(out) + return out +} diff --git a/k8-operator/k8-operator/cmd/main.go b/k8-operator/k8-operator/cmd/main.go new file mode 100644 index 000000000..3e706edbe --- /dev/null +++ b/k8-operator/k8-operator/cmd/main.go @@ -0,0 +1,258 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "crypto/tls" + "flag" + "os" + "path/filepath" + + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) + // to ensure that exec-entrypoint and run can make use of them. + _ "k8s.io/client-go/plugin/pkg/client/auth" + + "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/certwatcher" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/metrics/filters" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + "sigs.k8s.io/controller-runtime/pkg/webhook" + + secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + "github.com/Infisical/infisical/k8-operator/internal/controller" + // +kubebuilder:scaffold:imports +) + +var ( + scheme = runtime.NewScheme() + setupLog = ctrl.Log.WithName("setup") +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + + utilruntime.Must(secretsv1alpha1.AddToScheme(scheme)) + // +kubebuilder:scaffold:scheme +} + +// nolint:gocyclo +func main() { + var metricsAddr string + var metricsCertPath, metricsCertName, metricsCertKey string + var webhookCertPath, webhookCertName, webhookCertKey string + var enableLeaderElection bool + var probeAddr string + var secureMetrics bool + var enableHTTP2 bool + var tlsOpts []func(*tls.Config) + flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ + "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") + flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") + flag.BoolVar(&enableLeaderElection, "leader-elect", false, + "Enable leader election for controller manager. "+ + "Enabling this will ensure there is only one active controller manager.") + flag.BoolVar(&secureMetrics, "metrics-secure", true, + "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") + flag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.") + flag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.") + flag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.") + flag.StringVar(&metricsCertPath, "metrics-cert-path", "", + "The directory that contains the metrics server certificate.") + flag.StringVar(&metricsCertName, "metrics-cert-name", "tls.crt", "The name of the metrics server certificate file.") + flag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", "The name of the metrics server key file.") + flag.BoolVar(&enableHTTP2, "enable-http2", false, + "If set, HTTP/2 will be enabled for the metrics and webhook servers") + opts := zap.Options{ + Development: true, + } + opts.BindFlags(flag.CommandLine) + flag.Parse() + + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + + // if the enable-http2 flag is false (the default), http/2 should be disabled + // due to its vulnerabilities. More specifically, disabling http/2 will + // prevent from being vulnerable to the HTTP/2 Stream Cancellation and + // Rapid Reset CVEs. For more information see: + // - https://github.com/advisories/GHSA-qppj-fm5r-hxr3 + // - https://github.com/advisories/GHSA-4374-p667-p6c8 + disableHTTP2 := func(c *tls.Config) { + setupLog.Info("disabling http/2") + c.NextProtos = []string{"http/1.1"} + } + + if !enableHTTP2 { + tlsOpts = append(tlsOpts, disableHTTP2) + } + + // Create watchers for metrics and webhooks certificates + var metricsCertWatcher, webhookCertWatcher *certwatcher.CertWatcher + + // Initial webhook TLS options + webhookTLSOpts := tlsOpts + + if len(webhookCertPath) > 0 { + setupLog.Info("Initializing webhook certificate watcher using provided certificates", + "webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName, "webhook-cert-key", webhookCertKey) + + var err error + webhookCertWatcher, err = certwatcher.New( + filepath.Join(webhookCertPath, webhookCertName), + filepath.Join(webhookCertPath, webhookCertKey), + ) + if err != nil { + setupLog.Error(err, "Failed to initialize webhook certificate watcher") + os.Exit(1) + } + + webhookTLSOpts = append(webhookTLSOpts, func(config *tls.Config) { + config.GetCertificate = webhookCertWatcher.GetCertificate + }) + } + + webhookServer := webhook.NewServer(webhook.Options{ + TLSOpts: webhookTLSOpts, + }) + + // Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server. + // More info: + // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/metrics/server + // - https://book.kubebuilder.io/reference/metrics.html + metricsServerOptions := metricsserver.Options{ + BindAddress: metricsAddr, + SecureServing: secureMetrics, + TLSOpts: tlsOpts, + } + + if secureMetrics { + // FilterProvider is used to protect the metrics endpoint with authn/authz. + // These configurations ensure that only authorized users and service accounts + // can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info: + // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/metrics/filters#WithAuthenticationAndAuthorization + metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization + } + + // If the certificate is not specified, controller-runtime will automatically + // generate self-signed certificates for the metrics server. While convenient for development and testing, + // this setup is not recommended for production. + // + // TODO(user): If you enable certManager, uncomment the following lines: + // - [METRICS-WITH-CERTS] at config/default/kustomization.yaml to generate and use certificates + // managed by cert-manager for the metrics server. + // - [PROMETHEUS-WITH-CERTS] at config/prometheus/kustomization.yaml for TLS certification. + if len(metricsCertPath) > 0 { + setupLog.Info("Initializing metrics certificate watcher using provided certificates", + "metrics-cert-path", metricsCertPath, "metrics-cert-name", metricsCertName, "metrics-cert-key", metricsCertKey) + + var err error + metricsCertWatcher, err = certwatcher.New( + filepath.Join(metricsCertPath, metricsCertName), + filepath.Join(metricsCertPath, metricsCertKey), + ) + if err != nil { + setupLog.Error(err, "to initialize metrics certificate watcher", "error", err) + os.Exit(1) + } + + metricsServerOptions.TLSOpts = append(metricsServerOptions.TLSOpts, func(config *tls.Config) { + config.GetCertificate = metricsCertWatcher.GetCertificate + }) + } + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + Metrics: metricsServerOptions, + WebhookServer: webhookServer, + HealthProbeBindAddress: probeAddr, + LeaderElection: enableLeaderElection, + LeaderElectionID: "cf2b8c44.infisical.com", + // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily + // when the Manager ends. This requires the binary to immediately end when the + // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly + // speeds up voluntary leader transitions as the new leader don't have to wait + // LeaseDuration time first. + // + // In the default scaffold provided, the program ends immediately after + // the manager stops, so would be fine to enable this option. However, + // if you are doing or is intended to do any operation such as perform cleanups + // after the manager stops then its usage might be unsafe. + // LeaderElectionReleaseOnCancel: true, + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + os.Exit(1) + } + + if err := (&controller.InfisicalSecretReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "InfisicalSecret") + os.Exit(1) + } + if err := (&controller.InfisicalPushSecretSecretReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "InfisicalPushSecretSecret") + os.Exit(1) + } + if err := (&controller.InfisicalDynamicSecretReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create controller", "controller", "InfisicalDynamicSecret") + os.Exit(1) + } + // +kubebuilder:scaffold:builder + + if metricsCertWatcher != nil { + setupLog.Info("Adding metrics certificate watcher to manager") + if err := mgr.Add(metricsCertWatcher); err != nil { + setupLog.Error(err, "unable to add metrics certificate watcher to manager") + os.Exit(1) + } + } + + if webhookCertWatcher != nil { + setupLog.Info("Adding webhook certificate watcher to manager") + if err := mgr.Add(webhookCertWatcher); err != nil { + setupLog.Error(err, "unable to add webhook certificate watcher to manager") + os.Exit(1) + } + } + + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up health check") + os.Exit(1) + } + if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { + setupLog.Error(err, "unable to set up ready check") + os.Exit(1) + } + + setupLog.Info("starting manager") + if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { + setupLog.Error(err, "problem running manager") + os.Exit(1) + } +} diff --git a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_clustergenerators.yaml b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_clustergenerators.yaml new file mode 100644 index 000000000..0681f26ec --- /dev/null +++ b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_clustergenerators.yaml @@ -0,0 +1,96 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: clustergenerators.secrets.infisical.com +spec: + group: secrets.infisical.com + names: + kind: ClusterGenerator + listKind: ClusterGeneratorList + plural: clustergenerators + singular: clustergenerator + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterGenerator represents a cluster-wide generator + 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: + properties: + generator: + description: Generator the spec for this generator, must match the + kind. + properties: + passwordSpec: + description: PasswordSpec controls the behavior of the password + generator. + properties: + allowRepeat: + default: false + description: set allowRepeat to true to allow repeating characters. + type: boolean + digits: + description: |- + digits specifies the number of digits in the generated + password. If omitted it defaults to 25% of the length of the password + type: integer + length: + default: 24 + description: |- + Length of the password to be generated. + Defaults to 24 + type: integer + noUpper: + default: false + description: Set noUpper to disable uppercase characters + type: boolean + symbolCharacters: + description: |- + symbolCharacters specifies the special characters that should be used + in the generated password. + type: string + symbols: + description: |- + symbols specifies the number of symbol characters in the generated + password. If omitted it defaults to 25% of the length of the password + type: integer + type: object + uuidSpec: + description: UUIDSpec controls the behavior of the uuid generator. + type: object + type: object + kind: + description: Kind the kind of this generator. + enum: + - Password + - UUID + type: string + required: + - kind + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicaldynamicsecrets.yaml b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicaldynamicsecrets.yaml new file mode 100644 index 000000000..a71c90fd0 --- /dev/null +++ b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicaldynamicsecrets.yaml @@ -0,0 +1,309 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: infisicaldynamicsecrets.secrets.infisical.com +spec: + group: secrets.infisical.com + names: + kind: InfisicalDynamicSecret + listKind: InfisicalDynamicSecretList + plural: infisicaldynamicsecrets + singular: infisicaldynamicsecret + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: InfisicalDynamicSecret is the Schema for the infisicaldynamicsecrets + 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: InfisicalDynamicSecretSpec defines the desired state of InfisicalDynamicSecret. + properties: + authentication: + properties: + awsIamAuth: + properties: + identityId: + type: string + required: + - identityId + type: object + azureAuth: + properties: + identityId: + type: string + resource: + type: string + required: + - identityId + type: object + gcpIamAuth: + properties: + identityId: + type: string + serviceAccountKeyFilePath: + type: string + required: + - identityId + - serviceAccountKeyFilePath + type: object + gcpIdTokenAuth: + properties: + identityId: + type: string + required: + - identityId + type: object + kubernetesAuth: + properties: + autoCreateServiceAccountToken: + description: |- + Optionally automatically create a service account token for the configured service account. + If this is set to `true`, the operator will automatically create a service account token for the configured service account. This field is recommended in most cases. + type: boolean + identityId: + type: string + serviceAccountRef: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + serviceAccountTokenAudiences: + description: The audiences to use for the service account + token. This is only relevant if `autoCreateServiceAccountToken` + is true. + items: + type: string + type: array + required: + - identityId + - serviceAccountRef + type: object + universalAuth: + properties: + credentialsRef: + properties: + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret + is located + type: string + required: + - secretName + - secretNamespace + type: object + required: + - credentialsRef + type: object + type: object + dynamicSecret: + properties: + environmentSlug: + type: string + projectId: + type: string + secretName: + type: string + secretsPath: + type: string + required: + - environmentSlug + - projectId + - secretName + - secretsPath + type: object + hostAPI: + type: string + leaseRevocationPolicy: + type: string + leaseTTL: + type: string + managedSecretReference: + properties: + creationPolicy: + default: Orphan + description: |- + The Kubernetes Secret creation policy. + Enum with values: 'Owner', 'Orphan'. + Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. + Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted. + type: string + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret is located + type: string + secretType: + default: Opaque + description: 'The Kubernetes Secret type (experimental feature). + More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types' + type: string + template: + description: The template to transform the secret data + properties: + data: + additionalProperties: + type: string + description: The template key values + type: object + includeAllSecrets: + description: |- + This injects all retrieved secrets into the top level of your template. + Secrets defined in the template will take precedence over the injected ones. + type: boolean + type: object + required: + - secretName + - secretNamespace + type: object + tls: + properties: + caRef: + description: Reference to secret containing CA cert + properties: + key: + description: The name of the secret property with the CA certificate + value + type: string + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The namespace where the Kubernetes Secret is + located + type: string + required: + - key + - secretName + - secretNamespace + type: object + type: object + required: + - authentication + - dynamicSecret + - leaseRevocationPolicy + - leaseTTL + - managedSecretReference + type: object + status: + description: InfisicalDynamicSecretStatus defines the observed state of + InfisicalDynamicSecret. + properties: + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + dynamicSecretId: + type: string + lease: + properties: + creationTimestamp: + format: date-time + type: string + expiresAt: + format: date-time + type: string + id: + type: string + version: + format: int64 + type: integer + required: + - creationTimestamp + - expiresAt + - id + - version + type: object + maxTTL: + description: The MaxTTL can be null, if it's null, there's no max + TTL and we should never have to renew. + type: string + required: + - conditions + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml new file mode 100644 index 000000000..beed2fd50 --- /dev/null +++ b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml @@ -0,0 +1,305 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: infisicalpushsecrets.secrets.infisical.com +spec: + group: secrets.infisical.com + names: + kind: InfisicalPushSecret + listKind: InfisicalPushSecretList + plural: infisicalpushsecrets + singular: infisicalpushsecret + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: InfisicalPushSecret is the Schema for the infisicalpushsecrets + 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: InfisicalPushSecretSpec defines the desired state of InfisicalPushSecret + properties: + authentication: + properties: + awsIamAuth: + properties: + identityId: + type: string + required: + - identityId + type: object + azureAuth: + properties: + identityId: + type: string + resource: + type: string + required: + - identityId + type: object + gcpIamAuth: + properties: + identityId: + type: string + serviceAccountKeyFilePath: + type: string + required: + - identityId + - serviceAccountKeyFilePath + type: object + gcpIdTokenAuth: + properties: + identityId: + type: string + required: + - identityId + type: object + kubernetesAuth: + properties: + autoCreateServiceAccountToken: + description: |- + Optionally automatically create a service account token for the configured service account. + If this is set to `true`, the operator will automatically create a service account token for the configured service account. This field is recommended in most cases. + type: boolean + identityId: + type: string + serviceAccountRef: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + serviceAccountTokenAudiences: + description: The audiences to use for the service account + token. This is only relevant if `autoCreateServiceAccountToken` + is true. + items: + type: string + type: array + required: + - identityId + - serviceAccountRef + type: object + universalAuth: + properties: + credentialsRef: + properties: + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret + is located + type: string + required: + - secretName + - secretNamespace + type: object + required: + - credentialsRef + type: object + type: object + deletionPolicy: + type: string + destination: + properties: + environmentSlug: + type: string + projectId: + type: string + secretsPath: + type: string + required: + - environmentSlug + - projectId + - secretsPath + type: object + hostAPI: + description: Infisical host to pull secrets from + type: string + push: + properties: + generators: + items: + properties: + destinationSecretName: + type: string + generatorRef: + properties: + kind: + allOf: + - enum: + - Password + - UUID + - enum: + - Password + - UUID + description: Specify the Kind of the generator resource + type: string + name: + type: string + required: + - kind + - name + type: object + required: + - destinationSecretName + - generatorRef + type: object + type: array + secret: + properties: + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret is + located + type: string + template: + properties: + data: + additionalProperties: + type: string + description: The template key values + type: object + includeAllSecrets: + description: |- + This injects all retrieved secrets into the top level of your template. + Secrets defined in the template will take precedence over the injected ones. + type: boolean + type: object + required: + - secretName + - secretNamespace + type: object + type: object + resyncInterval: + type: string + tls: + properties: + caRef: + description: Reference to secret containing CA cert + properties: + key: + description: The name of the secret property with the CA certificate + value + type: string + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The namespace where the Kubernetes Secret is + located + type: string + required: + - key + - secretName + - secretNamespace + type: object + type: object + updatePolicy: + type: string + required: + - destination + - push + type: object + status: + description: InfisicalPushSecretStatus defines the observed state of InfisicalPushSecret + properties: + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + managedSecrets: + additionalProperties: + type: string + description: managed secrets is a map where the key is the ID, and + the value is the secret key (string[id], string[key] ) + type: object + required: + - conditions + - managedSecrets + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecretsecrets.yaml b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecretsecrets.yaml new file mode 100644 index 000000000..e94900eb2 --- /dev/null +++ b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecretsecrets.yaml @@ -0,0 +1,57 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: infisicalpushsecretsecrets.secrets.infisical.com +spec: + group: secrets.infisical.com + names: + kind: InfisicalPushSecretSecret + listKind: InfisicalPushSecretSecretList + plural: infisicalpushsecretsecrets + singular: infisicalpushsecretsecret + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: InfisicalPushSecretSecret is the Schema for the infisicalpushsecretsecrets + 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: spec defines the desired state of InfisicalPushSecretSecret + properties: + foo: + description: foo is an example field of InfisicalPushSecretSecret. + Edit infisicalpushsecretsecret_types.go to remove/update + type: string + type: object + status: + description: status defines the observed state of InfisicalPushSecretSecret + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml new file mode 100644 index 000000000..c1bc91f59 --- /dev/null +++ b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml @@ -0,0 +1,503 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: infisicalsecrets.secrets.infisical.com +spec: + group: secrets.infisical.com + names: + kind: InfisicalSecret + listKind: InfisicalSecretList + plural: infisicalsecrets + singular: infisicalsecret + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: InfisicalSecret is the Schema for the infisicalsecrets 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: InfisicalSecretSpec defines the desired state of InfisicalSecret + properties: + authentication: + properties: + awsIamAuth: + properties: + identityId: + type: string + secretsScope: + properties: + envSlug: + type: string + projectSlug: + type: string + recursive: + type: boolean + secretsPath: + type: string + required: + - envSlug + - projectSlug + - secretsPath + type: object + required: + - identityId + - secretsScope + type: object + azureAuth: + properties: + identityId: + type: string + resource: + type: string + secretsScope: + properties: + envSlug: + type: string + projectSlug: + type: string + recursive: + type: boolean + secretsPath: + type: string + required: + - envSlug + - projectSlug + - secretsPath + type: object + required: + - identityId + - secretsScope + type: object + gcpIamAuth: + properties: + identityId: + type: string + secretsScope: + properties: + envSlug: + type: string + projectSlug: + type: string + recursive: + type: boolean + secretsPath: + type: string + required: + - envSlug + - projectSlug + - secretsPath + type: object + serviceAccountKeyFilePath: + type: string + required: + - identityId + - secretsScope + - serviceAccountKeyFilePath + type: object + gcpIdTokenAuth: + properties: + identityId: + type: string + secretsScope: + properties: + envSlug: + type: string + projectSlug: + type: string + recursive: + type: boolean + secretsPath: + type: string + required: + - envSlug + - projectSlug + - secretsPath + type: object + required: + - identityId + - secretsScope + type: object + kubernetesAuth: + properties: + autoCreateServiceAccountToken: + description: |- + Optionally automatically create a service account token for the configured service account. + If this is set to `true`, the operator will automatically create a service account token for the configured service account. + type: boolean + identityId: + type: string + secretsScope: + properties: + envSlug: + type: string + projectSlug: + type: string + recursive: + type: boolean + secretsPath: + type: string + required: + - envSlug + - projectSlug + - secretsPath + type: object + serviceAccountRef: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + serviceAccountTokenAudiences: + description: The audiences to use for the service account + token. This is only relevant if `autoCreateServiceAccountToken` + is true. + items: + type: string + type: array + required: + - identityId + - secretsScope + - serviceAccountRef + type: object + serviceAccount: + properties: + environmentName: + type: string + projectId: + type: string + serviceAccountSecretReference: + properties: + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret + is located + type: string + required: + - secretName + - secretNamespace + type: object + required: + - environmentName + - projectId + - serviceAccountSecretReference + type: object + serviceToken: + properties: + secretsScope: + properties: + envSlug: + type: string + recursive: + type: boolean + secretsPath: + type: string + required: + - envSlug + - secretsPath + type: object + serviceTokenSecretReference: + properties: + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret + is located + type: string + required: + - secretName + - secretNamespace + type: object + required: + - secretsScope + - serviceTokenSecretReference + type: object + universalAuth: + properties: + credentialsRef: + properties: + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret + is located + type: string + required: + - secretName + - secretNamespace + type: object + secretsScope: + properties: + envSlug: + type: string + projectSlug: + type: string + recursive: + type: boolean + secretsPath: + type: string + required: + - envSlug + - projectSlug + - secretsPath + type: object + required: + - credentialsRef + - secretsScope + type: object + type: object + hostAPI: + description: Infisical host to pull secrets from + type: string + managedKubeConfigMapReferences: + items: + properties: + configMapName: + description: The name of the Kubernetes ConfigMap + type: string + configMapNamespace: + description: The namespace where the Kubernetes ConfigMap is + located + type: string + creationPolicy: + default: Orphan + description: |- + The Kubernetes ConfigMap creation policy. + Enum with values: 'Owner', 'Orphan'. + Owner creates the config map and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. + Orphan will not set the config map owner. This will result in the config map being orphaned and not deleted when the resource is deleted. + type: string + template: + description: The template to transform the secret data + properties: + data: + additionalProperties: + type: string + description: The template key values + type: object + includeAllSecrets: + description: |- + This injects all retrieved secrets into the top level of your template. + Secrets defined in the template will take precedence over the injected ones. + type: boolean + type: object + required: + - configMapName + - configMapNamespace + type: object + type: array + managedKubeSecretReferences: + items: + properties: + creationPolicy: + default: Orphan + description: |- + The Kubernetes Secret creation policy. + Enum with values: 'Owner', 'Orphan'. + Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. + Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted. + type: string + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret is located + type: string + secretType: + default: Opaque + description: 'The Kubernetes Secret type (experimental feature). + More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types' + type: string + template: + description: The template to transform the secret data + properties: + data: + additionalProperties: + type: string + description: The template key values + type: object + includeAllSecrets: + description: |- + This injects all retrieved secrets into the top level of your template. + Secrets defined in the template will take precedence over the injected ones. + type: boolean + type: object + required: + - secretName + - secretNamespace + type: object + type: array + managedSecretReference: + properties: + creationPolicy: + default: Orphan + description: |- + The Kubernetes Secret creation policy. + Enum with values: 'Owner', 'Orphan'. + Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. + Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted. + type: string + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret is located + type: string + secretType: + default: Opaque + description: 'The Kubernetes Secret type (experimental feature). + More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types' + type: string + template: + description: The template to transform the secret data + properties: + data: + additionalProperties: + type: string + description: The template key values + type: object + includeAllSecrets: + description: |- + This injects all retrieved secrets into the top level of your template. + Secrets defined in the template will take precedence over the injected ones. + type: boolean + type: object + required: + - secretName + - secretNamespace + type: object + resyncInterval: + default: 60 + type: integer + tls: + properties: + caRef: + description: Reference to secret containing CA cert + properties: + key: + description: The name of the secret property with the CA certificate + value + type: string + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The namespace where the Kubernetes Secret is + located + type: string + required: + - key + - secretName + - secretNamespace + type: object + type: object + tokenSecretReference: + properties: + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret is located + type: string + required: + - secretName + - secretNamespace + type: object + required: + - resyncInterval + type: object + status: + description: InfisicalSecretStatus defines the observed state of InfisicalSecret + properties: + conditions: + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + required: + - conditions + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_passwords.yaml b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_passwords.yaml new file mode 100644 index 000000000..788e077a6 --- /dev/null +++ b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_passwords.yaml @@ -0,0 +1,79 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: passwords.secrets.infisical.com +spec: + group: secrets.infisical.com + names: + kind: Password + listKind: PasswordList + plural: passwords + singular: password + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + Password generates a random password based on the + configuration parameters in spec. + You can specify the length, characterset and other attributes. + 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: PasswordSpec controls the behavior of the password generator. + properties: + allowRepeat: + default: false + description: set allowRepeat to true to allow repeating characters. + type: boolean + digits: + description: |- + digits specifies the number of digits in the generated + password. If omitted it defaults to 25% of the length of the password + type: integer + length: + default: 24 + description: |- + Length of the password to be generated. + Defaults to 24 + type: integer + noUpper: + default: false + description: Set noUpper to disable uppercase characters + type: boolean + symbolCharacters: + description: |- + symbolCharacters specifies the special characters that should be used + in the generated password. + type: string + symbols: + description: |- + symbols specifies the number of symbol characters in the generated + password. If omitted it defaults to 25% of the length of the password + type: integer + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_uuids.yaml b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_uuids.yaml new file mode 100644 index 000000000..659dfc7ac --- /dev/null +++ b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_uuids.yaml @@ -0,0 +1,46 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.18.0 + name: uuids.secrets.infisical.com +spec: + group: secrets.infisical.com + names: + kind: UUID + listKind: UUIDList + plural: uuids + singular: uuid + scope: Namespaced + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: UUID generates a version 4 UUID (e56657e3-764f-11ef-a397-65231a88c216). + 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: UUIDSpec controls the behavior of the uuid generator. + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/k8-operator/k8-operator/config/crd/kustomization.yaml b/k8-operator/k8-operator/config/crd/kustomization.yaml new file mode 100644 index 000000000..a3018a690 --- /dev/null +++ b/k8-operator/k8-operator/config/crd/kustomization.yaml @@ -0,0 +1,18 @@ +# 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/secrets.infisical.com_infisicalsecrets.yaml +- bases/secrets.infisical.com_infisicalpushsecretsecrets.yaml +- bases/secrets.infisical.com_infisicaldynamicsecrets.yaml +# +kubebuilder:scaffold:crdkustomizeresource + +patches: +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. +# patches here are for enabling the conversion webhook for each CRD +# +kubebuilder:scaffold:crdkustomizewebhookpatch + +# [WEBHOOK] To enable webhook, uncomment the following section +# the following config is for teaching kustomize how to do kustomization for CRDs. +#configurations: +#- kustomizeconfig.yaml diff --git a/k8-operator/k8-operator/config/crd/kustomizeconfig.yaml b/k8-operator/k8-operator/config/crd/kustomizeconfig.yaml new file mode 100644 index 000000000..ec5c150a9 --- /dev/null +++ b/k8-operator/k8-operator/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/k8-operator/k8-operator/config/default/cert_metrics_manager_patch.yaml b/k8-operator/k8-operator/config/default/cert_metrics_manager_patch.yaml new file mode 100644 index 000000000..d97501553 --- /dev/null +++ b/k8-operator/k8-operator/config/default/cert_metrics_manager_patch.yaml @@ -0,0 +1,30 @@ +# This patch adds the args, volumes, and ports to allow the manager to use the metrics-server certs. + +# Add the volumeMount for the metrics-server certs +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + mountPath: /tmp/k8s-metrics-server/metrics-certs + name: metrics-certs + readOnly: true + +# Add the --metrics-cert-path argument for the metrics server +- op: add + path: /spec/template/spec/containers/0/args/- + value: --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs + +# Add the metrics-server certs volume configuration +- op: add + path: /spec/template/spec/volumes/- + value: + name: metrics-certs + secret: + secretName: metrics-server-cert + optional: false + items: + - key: ca.crt + path: ca.crt + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key diff --git a/k8-operator/k8-operator/config/default/kustomization.yaml b/k8-operator/k8-operator/config/default/kustomization.yaml new file mode 100644 index 000000000..8eda77014 --- /dev/null +++ b/k8-operator/k8-operator/config/default/kustomization.yaml @@ -0,0 +1,234 @@ +# Adds namespace to all resources. +namespace: k8-operator-system + +# Value of this field is prepended to the +# names of all resources, e.g. a deployment named +# "wordpress" becomes "alices-wordpress". +# Note that it should also match with the prefix (text before '-') of the namespace +# field above. +namePrefix: k8-operator- + +# Labels to add to all resources and selectors. +#labels: +#- includeSelectors: true +# pairs: +# someName: someValue + +resources: +- ../crd +- ../rbac +- ../manager +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- ../webhook +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. +#- ../certmanager +# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. +#- ../prometheus +# [METRICS] Expose the controller manager metrics service. +- metrics_service.yaml +# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy. +# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics. +# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will +# be able to communicate with the Webhook Server. +#- ../network-policy + +# Uncomment the patches line if you enable Metrics +patches: +# [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. +# More info: https://book.kubebuilder.io/reference/metrics +- path: manager_metrics_patch.yaml + target: + kind: Deployment + +# Uncomment the patches line if you enable Metrics and CertManager +# [METRICS-WITH-CERTS] To enable metrics protected with certManager, uncomment the following line. +# This patch will protect the metrics with certManager self-signed certs. +#- path: cert_metrics_manager_patch.yaml +# target: +# kind: Deployment + +# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in +# crd/kustomization.yaml +#- path: manager_webhook_patch.yaml +# target: +# kind: Deployment + +# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. +# Uncomment the following replacements to add the cert-manager CA injection annotations +#replacements: +# - source: # Uncomment the following block to enable certificates for metrics +# kind: Service +# version: v1 +# name: controller-manager-metrics-service +# fieldPath: metadata.name +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: metrics-certs +# fieldPaths: +# - spec.dnsNames.0 +# - spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - select: # Uncomment the following to set the Service name for TLS config in Prometheus ServiceMonitor +# kind: ServiceMonitor +# group: monitoring.coreos.com +# version: v1 +# name: controller-manager-metrics-monitor +# fieldPaths: +# - spec.endpoints.0.tlsConfig.serverName +# options: +# delimiter: '.' +# index: 0 +# create: true + +# - source: +# kind: Service +# version: v1 +# name: controller-manager-metrics-service +# fieldPath: metadata.namespace +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: metrics-certs +# fieldPaths: +# - spec.dnsNames.0 +# - spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true +# - select: # Uncomment the following to set the Service namespace for TLS in Prometheus ServiceMonitor +# kind: ServiceMonitor +# group: monitoring.coreos.com +# version: v1 +# name: controller-manager-metrics-monitor +# fieldPaths: +# - spec.endpoints.0.tlsConfig.serverName +# options: +# delimiter: '.' +# index: 1 +# create: true + +# - source: # Uncomment the following block if you have any webhook +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.name # Name of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - source: +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.namespace # Namespace of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true + +# - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # This name should match the one in certificate.yaml +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true + +# - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting ) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true + +# - source: # Uncomment the following block if you have a ConversionWebhook (--conversion) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionns +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionname diff --git a/k8-operator/k8-operator/config/default/manager_metrics_patch.yaml b/k8-operator/k8-operator/config/default/manager_metrics_patch.yaml new file mode 100644 index 000000000..2aaef6536 --- /dev/null +++ b/k8-operator/k8-operator/config/default/manager_metrics_patch.yaml @@ -0,0 +1,4 @@ +# This patch adds the args to allow exposing the metrics endpoint using HTTPS +- op: add + path: /spec/template/spec/containers/0/args/0 + value: --metrics-bind-address=:8443 diff --git a/k8-operator/k8-operator/config/default/metrics_service.yaml b/k8-operator/k8-operator/config/default/metrics_service.yaml new file mode 100644 index 000000000..cf99575a7 --- /dev/null +++ b/k8-operator/k8-operator/config/default/metrics_service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: controller-manager-metrics-service + namespace: system +spec: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: 8443 + selector: + control-plane: controller-manager + app.kubernetes.io/name: k8-operator diff --git a/k8-operator/k8-operator/config/manager/kustomization.yaml b/k8-operator/k8-operator/config/manager/kustomization.yaml new file mode 100644 index 000000000..5c5f0b84c --- /dev/null +++ b/k8-operator/k8-operator/config/manager/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- manager.yaml diff --git a/k8-operator/k8-operator/config/manager/manager.yaml b/k8-operator/k8-operator/config/manager/manager.yaml new file mode 100644 index 000000000..eb41eff84 --- /dev/null +++ b/k8-operator/k8-operator/config/manager/manager.yaml @@ -0,0 +1,99 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: system +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: controller-manager + namespace: system + labels: + control-plane: controller-manager + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize +spec: + selector: + matchLabels: + control-plane: controller-manager + app.kubernetes.io/name: k8-operator + replicas: 1 + template: + metadata: + annotations: + kubectl.kubernetes.io/default-container: manager + labels: + control-plane: controller-manager + app.kubernetes.io/name: k8-operator + spec: + # TODO(user): Uncomment the following code to configure the nodeAffinity expression + # according to the platforms which are supported by your solution. + # It is considered best practice to support multiple architectures. You can + # build your manager image using the makefile target docker-buildx. + # affinity: + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: kubernetes.io/arch + # operator: In + # values: + # - amd64 + # - arm64 + # - ppc64le + # - s390x + # - key: kubernetes.io/os + # operator: In + # values: + # - linux + securityContext: + # Projects are configured by default to adhere to the "restricted" Pod Security Standards. + # This ensures that deployments meet the highest security requirements for Kubernetes. + # For more details, see: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + containers: + - command: + - /manager + args: + - --leader-elect + - --health-probe-bind-address=:8081 + image: controller:latest + name: manager + ports: [] + securityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + # TODO(user): Configure the resources accordingly based on the project requirements. + # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + volumeMounts: [] + volumes: [] + serviceAccountName: controller-manager + terminationGracePeriodSeconds: 10 diff --git a/k8-operator/k8-operator/config/network-policy/allow-metrics-traffic.yaml b/k8-operator/k8-operator/config/network-policy/allow-metrics-traffic.yaml new file mode 100644 index 000000000..21eed7944 --- /dev/null +++ b/k8-operator/k8-operator/config/network-policy/allow-metrics-traffic.yaml @@ -0,0 +1,27 @@ +# This NetworkPolicy allows ingress traffic +# with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those +# namespaces are able to gather data from the metrics endpoint. +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: allow-metrics-traffic + namespace: system +spec: + podSelector: + matchLabels: + control-plane: controller-manager + app.kubernetes.io/name: k8-operator + policyTypes: + - Ingress + ingress: + # This allows ingress traffic from any namespace with the label metrics: enabled + - from: + - namespaceSelector: + matchLabels: + metrics: enabled # Only from namespaces with this label + ports: + - port: 8443 + protocol: TCP diff --git a/k8-operator/k8-operator/config/network-policy/kustomization.yaml b/k8-operator/k8-operator/config/network-policy/kustomization.yaml new file mode 100644 index 000000000..ec0fb5e57 --- /dev/null +++ b/k8-operator/k8-operator/config/network-policy/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- allow-metrics-traffic.yaml diff --git a/k8-operator/k8-operator/config/prometheus/kustomization.yaml b/k8-operator/k8-operator/config/prometheus/kustomization.yaml new file mode 100644 index 000000000..fdc5481b1 --- /dev/null +++ b/k8-operator/k8-operator/config/prometheus/kustomization.yaml @@ -0,0 +1,11 @@ +resources: +- monitor.yaml + +# [PROMETHEUS-WITH-CERTS] The following patch configures the ServiceMonitor in ../prometheus +# to securely reference certificates created and managed by cert-manager. +# Additionally, ensure that you uncomment the [METRICS WITH CERTMANAGER] patch under config/default/kustomization.yaml +# to mount the "metrics-server-cert" secret in the Manager Deployment. +#patches: +# - path: monitor_tls_patch.yaml +# target: +# kind: ServiceMonitor diff --git a/k8-operator/k8-operator/config/prometheus/monitor.yaml b/k8-operator/k8-operator/config/prometheus/monitor.yaml new file mode 100644 index 000000000..abaef6489 --- /dev/null +++ b/k8-operator/k8-operator/config/prometheus/monitor.yaml @@ -0,0 +1,27 @@ +# Prometheus Monitor Service (Metrics) +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: controller-manager-metrics-monitor + namespace: system +spec: + endpoints: + - path: /metrics + port: https # Ensure this is the name of the port that exposes HTTPS metrics + scheme: https + bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + tlsConfig: + # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables + # certificate verification, exposing the system to potential man-in-the-middle attacks. + # For production environments, it is recommended to use cert-manager for automatic TLS certificate management. + # To apply this configuration, enable cert-manager and use the patch located at config/prometheus/servicemonitor_tls_patch.yaml, + # which securely references the certificate from the 'metrics-server-cert' secret. + insecureSkipVerify: true + selector: + matchLabels: + control-plane: controller-manager + app.kubernetes.io/name: k8-operator diff --git a/k8-operator/k8-operator/config/prometheus/monitor_tls_patch.yaml b/k8-operator/k8-operator/config/prometheus/monitor_tls_patch.yaml new file mode 100644 index 000000000..5bf84ce0d --- /dev/null +++ b/k8-operator/k8-operator/config/prometheus/monitor_tls_patch.yaml @@ -0,0 +1,19 @@ +# Patch for Prometheus ServiceMonitor to enable secure TLS configuration +# using certificates managed by cert-manager +- op: replace + path: /spec/endpoints/0/tlsConfig + value: + # SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize + serverName: SERVICE_NAME.SERVICE_NAMESPACE.svc + insecureSkipVerify: false + ca: + secret: + name: metrics-server-cert + key: ca.crt + cert: + secret: + name: metrics-server-cert + key: tls.crt + keySecret: + name: metrics-server-cert + key: tls.key diff --git a/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_admin_role.yaml b/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_admin_role.yaml new file mode 100644 index 000000000..8bcd0e68c --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project k8-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over secrets.infisical.com. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: infisicaldynamicsecret-admin-role +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets + verbs: + - '*' +- apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets/status + verbs: + - get diff --git a/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_editor_role.yaml b/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_editor_role.yaml new file mode 100644 index 000000000..4d3ffc985 --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_editor_role.yaml @@ -0,0 +1,33 @@ +# This rule is not used by the project k8-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the secrets.infisical.com. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: infisicaldynamicsecret-editor-role +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets/status + verbs: + - get diff --git a/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_viewer_role.yaml b/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_viewer_role.yaml new file mode 100644 index 000000000..d5cfc7be9 --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_viewer_role.yaml @@ -0,0 +1,29 @@ +# This rule is not used by the project k8-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to secrets.infisical.com resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: infisicaldynamicsecret-viewer-role +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets + verbs: + - get + - list + - watch +- apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets/status + verbs: + - get diff --git a/k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_admin_role.yaml b/k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_admin_role.yaml new file mode 100644 index 000000000..e8674cc6f --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project k8-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over secrets.infisical.com. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: infisicalpushsecretsecret-admin-role +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicalpushsecretsecrets + verbs: + - '*' +- apiGroups: + - secrets.infisical.com + resources: + - infisicalpushsecretsecrets/status + verbs: + - get diff --git a/k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_editor_role.yaml b/k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_editor_role.yaml new file mode 100644 index 000000000..97ab6dc66 --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_editor_role.yaml @@ -0,0 +1,33 @@ +# This rule is not used by the project k8-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the secrets.infisical.com. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: infisicalpushsecretsecret-editor-role +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicalpushsecretsecrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - secrets.infisical.com + resources: + - infisicalpushsecretsecrets/status + verbs: + - get diff --git a/k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_viewer_role.yaml b/k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_viewer_role.yaml new file mode 100644 index 000000000..4a7ae8346 --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_viewer_role.yaml @@ -0,0 +1,29 @@ +# This rule is not used by the project k8-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to secrets.infisical.com resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: infisicalpushsecretsecret-viewer-role +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicalpushsecretsecrets + verbs: + - get + - list + - watch +- apiGroups: + - secrets.infisical.com + resources: + - infisicalpushsecretsecrets/status + verbs: + - get diff --git a/k8-operator/k8-operator/config/rbac/infisicalsecret_admin_role.yaml b/k8-operator/k8-operator/config/rbac/infisicalsecret_admin_role.yaml new file mode 100644 index 000000000..1c5d88eed --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/infisicalsecret_admin_role.yaml @@ -0,0 +1,27 @@ +# This rule is not used by the project k8-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants full permissions ('*') over secrets.infisical.com. +# This role is intended for users authorized to modify roles and bindings within the cluster, +# enabling them to delegate specific permissions to other users or groups as needed. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: infisicalsecret-admin-role +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicalsecrets + verbs: + - '*' +- apiGroups: + - secrets.infisical.com + resources: + - infisicalsecrets/status + verbs: + - get diff --git a/k8-operator/k8-operator/config/rbac/infisicalsecret_editor_role.yaml b/k8-operator/k8-operator/config/rbac/infisicalsecret_editor_role.yaml new file mode 100644 index 000000000..a70abf3c4 --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/infisicalsecret_editor_role.yaml @@ -0,0 +1,33 @@ +# This rule is not used by the project k8-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the secrets.infisical.com. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: infisicalsecret-editor-role +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicalsecrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - secrets.infisical.com + resources: + - infisicalsecrets/status + verbs: + - get diff --git a/k8-operator/k8-operator/config/rbac/infisicalsecret_viewer_role.yaml b/k8-operator/k8-operator/config/rbac/infisicalsecret_viewer_role.yaml new file mode 100644 index 000000000..a5a724940 --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/infisicalsecret_viewer_role.yaml @@ -0,0 +1,29 @@ +# This rule is not used by the project k8-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to secrets.infisical.com resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: infisicalsecret-viewer-role +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicalsecrets + verbs: + - get + - list + - watch +- apiGroups: + - secrets.infisical.com + resources: + - infisicalsecrets/status + verbs: + - get diff --git a/k8-operator/k8-operator/config/rbac/kustomization.yaml b/k8-operator/k8-operator/config/rbac/kustomization.yaml new file mode 100644 index 000000000..d879dffa4 --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/kustomization.yaml @@ -0,0 +1,34 @@ +resources: +# All RBAC will be applied under this service account in +# the deployment namespace. You may comment out this resource +# if your manager will use a service account that exists at +# runtime. Be sure to update RoleBinding and ClusterRoleBinding +# subjects if changing service account names. +- service_account.yaml +- role.yaml +- role_binding.yaml +- leader_election_role.yaml +- leader_election_role_binding.yaml +# The following RBAC configurations are used to protect +# the metrics endpoint with authn/authz. These configurations +# ensure that only authorized users and service accounts +# can access the metrics endpoint. Comment the following +# permissions if you want to disable this protection. +# More info: https://book.kubebuilder.io/reference/metrics.html +- metrics_auth_role.yaml +- metrics_auth_role_binding.yaml +- metrics_reader_role.yaml +# For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by +# default, aiding admins in cluster management. Those roles are +# not used by the k8-operator itself. You can comment the following lines +# if you do not want those helpers be installed with your Project. +- infisicaldynamicsecret_admin_role.yaml +- infisicaldynamicsecret_editor_role.yaml +- infisicaldynamicsecret_viewer_role.yaml +- infisicalpushsecretsecret_admin_role.yaml +- infisicalpushsecretsecret_editor_role.yaml +- infisicalpushsecretsecret_viewer_role.yaml +- infisicalsecret_admin_role.yaml +- infisicalsecret_editor_role.yaml +- infisicalsecret_viewer_role.yaml + diff --git a/k8-operator/k8-operator/config/rbac/leader_election_role.yaml b/k8-operator/k8-operator/config/rbac/leader_election_role.yaml new file mode 100644 index 000000000..a86e00ed1 --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/leader_election_role.yaml @@ -0,0 +1,40 @@ +# permissions to do leader election. +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: leader-election-role +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch diff --git a/k8-operator/k8-operator/config/rbac/leader_election_role_binding.yaml b/k8-operator/k8-operator/config/rbac/leader_election_role_binding.yaml new file mode 100644 index 000000000..d662fc9de --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/leader_election_role_binding.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: leader-election-role +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/k8-operator/k8-operator/config/rbac/metrics_auth_role.yaml b/k8-operator/k8-operator/config/rbac/metrics_auth_role.yaml new file mode 100644 index 000000000..32d2e4ec6 --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/metrics_auth_role.yaml @@ -0,0 +1,17 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: metrics-auth-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create diff --git a/k8-operator/k8-operator/config/rbac/metrics_auth_role_binding.yaml b/k8-operator/k8-operator/config/rbac/metrics_auth_role_binding.yaml new file mode 100644 index 000000000..e775d67ff --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/metrics_auth_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: metrics-auth-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: metrics-auth-role +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/k8-operator/k8-operator/config/rbac/metrics_reader_role.yaml b/k8-operator/k8-operator/config/rbac/metrics_reader_role.yaml new file mode 100644 index 000000000..51a75db47 --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/metrics_reader_role.yaml @@ -0,0 +1,9 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: metrics-reader +rules: +- nonResourceURLs: + - "/metrics" + verbs: + - get diff --git a/k8-operator/k8-operator/config/rbac/role.yaml b/k8-operator/k8-operator/config/rbac/role.yaml new file mode 100644 index 000000000..ff39d7b3b --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/role.yaml @@ -0,0 +1,38 @@ +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: manager-role +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets + - infisicalpushsecretsecrets + - infisicalsecrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets/finalizers + - infisicalpushsecretsecrets/finalizers + - infisicalsecrets/finalizers + verbs: + - update +- apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets/status + - infisicalpushsecretsecrets/status + - infisicalsecrets/status + verbs: + - get + - patch + - update diff --git a/k8-operator/k8-operator/config/rbac/role_binding.yaml b/k8-operator/k8-operator/config/rbac/role_binding.yaml new file mode 100644 index 000000000..5e15ad6f4 --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/role_binding.yaml @@ -0,0 +1,15 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: manager-role +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/k8-operator/k8-operator/config/rbac/service_account.yaml b/k8-operator/k8-operator/config/rbac/service_account.yaml new file mode 100644 index 000000000..ed238fe99 --- /dev/null +++ b/k8-operator/k8-operator/config/rbac/service_account.yaml @@ -0,0 +1,8 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: controller-manager + namespace: system diff --git a/k8-operator/k8-operator/config/samples/kustomization.yaml b/k8-operator/k8-operator/config/samples/kustomization.yaml new file mode 100644 index 000000000..d5ed6c144 --- /dev/null +++ b/k8-operator/k8-operator/config/samples/kustomization.yaml @@ -0,0 +1,6 @@ +## Append samples of your project ## +resources: +- secrets_v1alpha1_infisicalsecret.yaml +- secrets_v1alpha1_infisicalpushsecretsecret.yaml +- secrets_v1alpha1_infisicaldynamicsecret.yaml +# +kubebuilder:scaffold:manifestskustomizesamples diff --git a/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicaldynamicsecret.yaml b/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicaldynamicsecret.yaml new file mode 100644 index 000000000..fd3b990b3 --- /dev/null +++ b/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicaldynamicsecret.yaml @@ -0,0 +1,9 @@ +apiVersion: secrets.infisical.com/v1alpha1 +kind: InfisicalDynamicSecret +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: infisicaldynamicsecret-sample +spec: + # TODO(user): Add fields here diff --git a/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalpushsecretsecret.yaml b/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalpushsecretsecret.yaml new file mode 100644 index 000000000..3370bcc4a --- /dev/null +++ b/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalpushsecretsecret.yaml @@ -0,0 +1,9 @@ +apiVersion: secrets.infisical.com/v1alpha1 +kind: InfisicalPushSecretSecret +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: infisicalpushsecretsecret-sample +spec: + # TODO(user): Add fields here diff --git a/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalsecret.yaml b/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalsecret.yaml new file mode 100644 index 000000000..029dd4ec0 --- /dev/null +++ b/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalsecret.yaml @@ -0,0 +1,9 @@ +apiVersion: secrets.infisical.com/v1alpha1 +kind: InfisicalSecret +metadata: + labels: + app.kubernetes.io/name: k8-operator + app.kubernetes.io/managed-by: kustomize + name: infisicalsecret-sample +spec: + # TODO(user): Add fields here diff --git a/k8-operator/k8-operator/go.mod b/k8-operator/k8-operator/go.mod new file mode 100644 index 000000000..d5c10e6c6 --- /dev/null +++ b/k8-operator/k8-operator/go.mod @@ -0,0 +1,97 @@ +module github.com/Infisical/infisical/k8-operator + +go 1.24.0 + +require ( + github.com/onsi/ginkgo/v2 v2.22.0 + github.com/onsi/gomega v1.36.1 + k8s.io/apimachinery v0.33.0 + k8s.io/client-go v0.33.0 + sigs.k8s.io/controller-runtime v0.21.0 +) + +require ( + cel.dev/expr v0.19.1 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-logr/logr v1.4.2 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-logr/zapr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/cel-go v0.23.2 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // 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/mailru/easyjson v0.7.7 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect + github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stoewer/go-strcase v1.3.0 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect + go.opentelemetry.io/otel v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect + go.opentelemetry.io/otel/metric v1.33.0 // indirect + go.opentelemetry.io/otel/sdk v1.33.0 // indirect + go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/proto/otlp v1.4.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/oauth2 v0.27.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/term v0.30.0 // indirect + golang.org/x/text v0.23.0 // indirect + golang.org/x/time v0.9.0 // indirect + golang.org/x/tools v0.26.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/grpc v1.68.1 // indirect + google.golang.org/protobuf v1.36.5 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.33.0 // indirect + k8s.io/apiextensions-apiserver v0.33.0 // indirect + k8s.io/apiserver v0.33.0 // indirect + k8s.io/component-base v0.33.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) diff --git a/k8-operator/k8-operator/go.sum b/k8-operator/k8-operator/go.sum new file mode 100644 index 000000000..14ef04932 --- /dev/null +++ b/k8-operator/k8-operator/go.sum @@ -0,0 +1,254 @@ +cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= +cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cpuguy83/go-md2man/v2 v2.0.4/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/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= +github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.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/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= +github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= +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/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/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +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/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= +github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= +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/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= +go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= +go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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= +k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU= +k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM= +k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs= +k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc= +k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ= +k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/apiserver v0.33.0 h1:QqcM6c+qEEjkOODHppFXRiw/cE2zP85704YrQ9YaBbc= +k8s.io/apiserver v0.33.0/go.mod h1:EixYOit0YTxt8zrO2kBU7ixAtxFce9gKGq367nFmqI8= +k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98= +k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg= +k8s.io/component-base v0.33.0 h1:Ot4PyJI+0JAD9covDhwLp9UNkUja209OzsJ4FzScBNk= +k8s.io/component-base v0.33.0/go.mod h1:aXYZLbw3kihdkOPMDhWbjGCO6sg+luw554KP51t8qCU= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= +sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/k8-operator/k8-operator/hack/boilerplate.go.txt b/k8-operator/k8-operator/hack/boilerplate.go.txt new file mode 100644 index 000000000..221dcbe0b --- /dev/null +++ b/k8-operator/k8-operator/hack/boilerplate.go.txt @@ -0,0 +1,15 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ \ No newline at end of file diff --git a/k8-operator/k8-operator/internal/api/api.go b/k8-operator/k8-operator/internal/api/api.go new file mode 100644 index 000000000..36edfa5c1 --- /dev/null +++ b/k8-operator/k8-operator/internal/api/api.go @@ -0,0 +1,148 @@ +package api + +import ( + "fmt" + + "github.com/go-resty/resty/v2" +) + +const USER_AGENT_NAME = "k8-operator" + +func CallGetServiceTokenDetailsV2(httpClient *resty.Client) (GetServiceTokenDetailsResponse, error) { + var tokenDetailsResponse GetServiceTokenDetailsResponse + response, err := httpClient. + R(). + SetResult(&tokenDetailsResponse). + SetHeader("User-Agent", USER_AGENT_NAME). + Get(fmt.Sprintf("%v/v2/service-token", API_HOST_URL)) + + if err != nil { + return GetServiceTokenDetailsResponse{}, fmt.Errorf("CallGetServiceTokenDetails: Unable to complete api request [err=%s]", err) + } + + if response.IsError() { + return GetServiceTokenDetailsResponse{}, fmt.Errorf("CallGetServiceTokenDetails: Unsuccessful response: [response=%s]", response) + } + + return tokenDetailsResponse, nil +} + +func CallGetServiceTokenAccountDetailsV2(httpClient *resty.Client) (ServiceAccountDetailsResponse, error) { + var serviceAccountDetailsResponse ServiceAccountDetailsResponse + response, err := httpClient. + R(). + SetResult(&serviceAccountDetailsResponse). + SetHeader("User-Agent", USER_AGENT_NAME). + Get(fmt.Sprintf("%v/v2/service-accounts/me", API_HOST_URL)) + + if err != nil { + return ServiceAccountDetailsResponse{}, fmt.Errorf("CallGetServiceTokenAccountDetailsV2: Unable to complete api request [err=%s]", err) + } + + if response.IsError() { + return ServiceAccountDetailsResponse{}, fmt.Errorf("CallGetServiceTokenAccountDetailsV2: Unsuccessful response: [response=%s]", response) + } + + return serviceAccountDetailsResponse, nil +} + +func CallUniversalMachineIdentityLogin(request MachineIdentityUniversalAuthLoginRequest) (MachineIdentityDetailsResponse, error) { + var machineIdentityDetailsResponse MachineIdentityDetailsResponse + + response, err := resty.New(). + R(). + SetResult(&machineIdentityDetailsResponse). + SetBody(request). + SetHeader("User-Agent", USER_AGENT_NAME). + Post(fmt.Sprintf("%v/v1/auth/universal-auth/login", API_HOST_URL)) + + if err != nil { + return MachineIdentityDetailsResponse{}, fmt.Errorf("CallUniversalMachineIdentityLogin: Unable to complete api request [err=%s]", err) + } + + if response.IsError() { + return MachineIdentityDetailsResponse{}, fmt.Errorf("CallUniversalMachineIdentityLogin: Unsuccessful response: [response=%s]", response) + } + + return machineIdentityDetailsResponse, nil +} + +func CallUniversalMachineIdentityRefreshAccessToken(request MachineIdentityUniversalAuthRefreshRequest) (MachineIdentityDetailsResponse, error) { + var universalAuthRefreshResponse MachineIdentityDetailsResponse + + response, err := resty.New(). + R(). + SetResult(&universalAuthRefreshResponse). + SetHeader("User-Agent", USER_AGENT_NAME). + SetBody(request). + Post(fmt.Sprintf("%v/v1/auth/token/renew", API_HOST_URL)) + + if err != nil { + return MachineIdentityDetailsResponse{}, fmt.Errorf("CallUniversalAuthRefreshAccessToken: Unable to complete api request [err=%s]", err) + } + + if response.IsError() { + return MachineIdentityDetailsResponse{}, fmt.Errorf("CallUniversalAuthRefreshAccessToken: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String()) + } + + return universalAuthRefreshResponse, nil +} + +func CallGetServiceAccountWorkspacePermissionsV2(httpClient *resty.Client) (ServiceAccountWorkspacePermissions, error) { + var serviceAccountWorkspacePermissionsResponse ServiceAccountWorkspacePermissions + response, err := httpClient. + R(). + SetResult(&serviceAccountWorkspacePermissionsResponse). + SetHeader("User-Agent", USER_AGENT_NAME). + Get(fmt.Sprintf("%v/v2/service-accounts//permissions/workspace", API_HOST_URL)) + + if err != nil { + return ServiceAccountWorkspacePermissions{}, fmt.Errorf("CallGetServiceAccountWorkspacePermissionsV2: Unable to complete api request [err=%s]", err) + } + + if response.IsError() { + return ServiceAccountWorkspacePermissions{}, fmt.Errorf("CallGetServiceAccountWorkspacePermissionsV2: Unsuccessful response: [response=%s]", response) + } + + return serviceAccountWorkspacePermissionsResponse, nil +} + +func CallGetServiceAccountKeysV2(httpClient *resty.Client, request GetServiceAccountKeysRequest) (GetServiceAccountKeysResponse, error) { + var serviceAccountKeysResponse GetServiceAccountKeysResponse + response, err := httpClient. + R(). + SetResult(&serviceAccountKeysResponse). + SetHeader("User-Agent", USER_AGENT_NAME). + Get(fmt.Sprintf("%v/v2/service-accounts/%v/keys", API_HOST_URL, request.ServiceAccountId)) + + if err != nil { + return GetServiceAccountKeysResponse{}, fmt.Errorf("CallGetServiceAccountKeysV2: Unable to complete api request [err=%s]", err) + } + + if response.IsError() { + return GetServiceAccountKeysResponse{}, fmt.Errorf("CallGetServiceAccountKeysV2: Unsuccessful response: [response=%s]", response) + } + + return serviceAccountKeysResponse, nil +} + +func CallGetProjectByID(httpClient *resty.Client, request GetProjectByIDRequest) (GetProjectByIDResponse, error) { + + var projectResponse GetProjectByIDResponse + + response, err := httpClient. + R().SetResult(&projectResponse). + SetHeader("User-Agent", USER_AGENT_NAME). + Get(fmt.Sprintf("%s/v1/workspace/%s", API_HOST_URL, request.ProjectID)) + + if err != nil { + return GetProjectByIDResponse{}, fmt.Errorf("CallGetProject: Unable to complete api request [err=%s]", err) + } + + if response.IsError() { + return GetProjectByIDResponse{}, fmt.Errorf("CallGetProject: Unsuccessful response: [response=%s]", response) + } + + return projectResponse, nil + +} diff --git a/k8-operator/k8-operator/internal/api/models.go b/k8-operator/k8-operator/internal/api/models.go new file mode 100644 index 000000000..2128aac2a --- /dev/null +++ b/k8-operator/k8-operator/internal/api/models.go @@ -0,0 +1,208 @@ +package api + +import ( + "time" + + "github.com/Infisical/infisical/k8-operator/internal/model" +) + +type GetEncryptedWorkspaceKeyRequest struct { + WorkspaceId string `json:"workspaceId"` +} + +type GetEncryptedWorkspaceKeyResponse struct { + ID string `json:"_id"` + EncryptedKey string `json:"encryptedKey"` + Nonce string `json:"nonce"` + Sender struct { + ID string `json:"_id"` + Email string `json:"email"` + RefreshVersion int `json:"refreshVersion"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + V int `json:"__v"` + FirstName string `json:"firstName"` + LastName string `json:"lastName"` + PublicKey string `json:"publicKey"` + } `json:"sender"` + Receiver string `json:"receiver"` + Workspace string `json:"workspace"` + V int `json:"__v"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type GetEncryptedSecretsV3Request struct { + Environment string `json:"environment"` + WorkspaceId string `json:"workspaceId"` + Recursive bool `json:"recursive"` + SecretPath string `json:"secretPath"` + IncludeImport bool `json:"include_imports"` + ETag string `json:"etag,omitempty"` +} + +type EncryptedSecretV3 struct { + ID string `json:"_id"` + Version int `json:"version"` + Workspace string `json:"workspace"` + Type string `json:"type"` + Tags []struct { + ID string `json:"_id"` + Name string `json:"name"` + Slug string `json:"slug"` + Workspace string `json:"workspace"` + } `json:"tags"` + Environment string `json:"environment"` + SecretKeyCiphertext string `json:"secretKeyCiphertext"` + SecretKeyIV string `json:"secretKeyIV"` + SecretKeyTag string `json:"secretKeyTag"` + SecretValueCiphertext string `json:"secretValueCiphertext"` + SecretValueIV string `json:"secretValueIV"` + SecretValueTag string `json:"secretValueTag"` + SecretCommentCiphertext string `json:"secretCommentCiphertext"` + SecretCommentIV string `json:"secretCommentIV"` + SecretCommentTag string `json:"secretCommentTag"` + Algorithm string `json:"algorithm"` + KeyEncoding string `json:"keyEncoding"` + Folder string `json:"folder"` + V int `json:"__v"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type DecryptedSecretV3 struct { + ID string `json:"id"` + Workspace string `json:"workspace"` + Environment string `json:"environment"` + Version int `json:"version"` + Type string `json:"string"` + SecretKey string `json:"secretKey"` + SecretValue string `json:"secretValue"` + SecretComment string `json:"secretComment"` +} + +type ImportedSecretV3 struct { + Environment string `json:"environment"` + FolderId string `json:"folderId"` + SecretPath string `json:"secretPath"` + Secrets []EncryptedSecretV3 `json:"secrets"` +} + +type ImportedRawSecretV3 struct { + Environment string `json:"environment"` + FolderId string `json:"folderId"` + SecretPath string `json:"secretPath"` + Secrets []DecryptedSecretV3 `json:"secrets"` +} + +type GetEncryptedSecretsV3Response struct { + Secrets []EncryptedSecretV3 `json:"secrets"` + ImportedSecrets []ImportedSecretV3 `json:"imports,omitempty"` + Modified bool `json:"modified,omitempty"` + ETag string `json:"ETag,omitempty"` +} + +type GetDecryptedSecretsV3Response struct { + Secrets []DecryptedSecretV3 `json:"secrets"` + ETag string `json:"ETag,omitempty"` + Modified bool `json:"modified,omitempty"` + Imports []ImportedRawSecretV3 `json:"imports,omitempty"` +} + +type GetDecryptedSecretsV3Request struct { + ProjectID string `json:"workspaceId"` + ProjectSlug string `json:"workspaceSlug"` + Environment string `json:"environment"` + SecretPath string `json:"secretPath"` + Recursive bool `json:"recursive"` + ExpandSecretReferences bool `json:"expandSecretReferences"` + ETag string `json:"etag,omitempty"` +} + +type GetServiceTokenDetailsResponse struct { + ID string `json:"_id"` + Name string `json:"name"` + Workspace string `json:"workspace"` + Environment string `json:"environment"` + EncryptedKey string `json:"encryptedKey"` + Iv string `json:"iv"` + Tag string `json:"tag"` + SecretPath string `json:"secretPath"` +} + +type ServiceAccountDetailsResponse struct { + ServiceAccount struct { + ID string `json:"_id"` + Name string `json:"name"` + Organization string `json:"organization"` + PublicKey string `json:"publicKey"` + LastUsed time.Time `json:"lastUsed"` + ExpiresAt time.Time `json:"expiresAt"` + } `json:"serviceAccount"` +} + +type MachineIdentityDetailsResponse struct { + AccessToken string `json:"accessToken"` + ExpiresIn int `json:"expiresIn"` + AccessTokenMaxTTL int `json:"accessTokenMaxTTL"` + TokenType string `json:"tokenType"` +} + +type ServiceAccountWorkspacePermission struct { + ID string `json:"_id"` + ServiceAccount string `json:"serviceAccount"` + Workspace struct { + ID string `json:"_id"` + Name string `json:"name"` + AutoCapitalization bool `json:"autoCapitalization"` + Organization string `json:"organization"` + Environments []struct { + Name string `json:"name"` + Slug string `json:"slug"` + ID string `json:"_id"` + } `json:"environments"` + } `json:"workspace"` + Environment string `json:"environment"` + Read bool `json:"read"` + Write bool `json:"write"` +} + +type ServiceAccountWorkspacePermissions struct { + ServiceAccountWorkspacePermission []ServiceAccountWorkspacePermissions `json:"serviceAccountWorkspacePermissions"` +} + +type GetServiceAccountKeysRequest struct { + ServiceAccountId string `json:"id"` +} + +type MachineIdentityUniversalAuthLoginRequest struct { + ClientId string `json:"clientId"` + ClientSecret string `json:"clientSecret"` +} + +type MachineIdentityUniversalAuthRefreshRequest struct { + AccessToken string `json:"accessToken"` +} + +type ServiceAccountKey struct { + ID string `json:"_id"` + EncryptedKey string `json:"encryptedKey"` + Nonce string `json:"nonce"` + Sender string `json:"sender"` + ServiceAccount string `json:"serviceAccount"` + Workspace string `json:"workspace"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type GetServiceAccountKeysResponse struct { + ServiceAccountKeys []ServiceAccountKey `json:"serviceAccountKeys"` +} + +type GetProjectByIDRequest struct { + ProjectID string +} + +type GetProjectByIDResponse struct { + Project model.Project `json:"workspace"` +} diff --git a/k8-operator/k8-operator/internal/api/variables.go b/k8-operator/k8-operator/internal/api/variables.go new file mode 100644 index 000000000..1dd255d42 --- /dev/null +++ b/k8-operator/k8-operator/internal/api/variables.go @@ -0,0 +1,4 @@ +package api + +var API_HOST_URL string = "https://app.infisical.com/api" +var API_CA_CERTIFICATE string = "" diff --git a/k8-operator/k8-operator/internal/constants/constants.go b/k8-operator/k8-operator/internal/constants/constants.go new file mode 100644 index 000000000..e5e2d8ff8 --- /dev/null +++ b/k8-operator/k8-operator/internal/constants/constants.go @@ -0,0 +1,42 @@ +package constants + +import "errors" + +const SERVICE_ACCOUNT_ACCESS_KEY = "serviceAccountAccessKey" +const SERVICE_ACCOUNT_PUBLIC_KEY = "serviceAccountPublicKey" +const SERVICE_ACCOUNT_PRIVATE_KEY = "serviceAccountPrivateKey" + +const INFISICAL_MACHINE_IDENTITY_CLIENT_ID = "clientId" +const INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET = "clientSecret" + +const INFISICAL_TOKEN_SECRET_KEY_NAME = "infisicalToken" +const SECRET_VERSION_ANNOTATION = "secrets.infisical.com/version" // used to set the version of secrets via Etag +const OPERATOR_SETTINGS_CONFIGMAP_NAME = "infisical-config" +const OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE = "infisical-operator-system" +const INFISICAL_DOMAIN = "https://app.infisical.com/api" + +const INFISICAL_PUSH_SECRET_FINALIZER_NAME = "pushsecret.secrets.infisical.com/finalizer" +const INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME = "dynamicsecret.secrets.infisical.com/finalizer" + +type PushSecretReplacePolicy string +type PushSecretDeletionPolicy string + +const ( + PUSH_SECRET_REPLACE_POLICY_ENABLED PushSecretReplacePolicy = "Replace" + PUSH_SECRET_DELETE_POLICY_ENABLED PushSecretDeletionPolicy = "Delete" +) + +type ManagedKubeResourceType string + +const ( + MANAGED_KUBE_RESOURCE_TYPE_SECRET ManagedKubeResourceType = "Secret" + MANAGED_KUBE_RESOURCE_TYPE_CONFIG_MAP ManagedKubeResourceType = "ConfigMap" +) + +type DynamicSecretLeaseRevocationPolicy string + +const ( + DYNAMIC_SECRET_LEASE_REVOCATION_POLICY_ENABLED DynamicSecretLeaseRevocationPolicy = "Revoke" +) + +var ErrInvalidLease = errors.New("invalid dynamic secret lease") diff --git a/k8-operator/k8-operator/internal/controller/infisicaldynamicsecret_controller.go b/k8-operator/k8-operator/internal/controller/infisicaldynamicsecret_controller.go new file mode 100644 index 000000000..0930a9064 --- /dev/null +++ b/k8-operator/k8-operator/internal/controller/infisicaldynamicsecret_controller.go @@ -0,0 +1,63 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" +) + +// InfisicalDynamicSecretReconciler reconciles a InfisicalDynamicSecret object +type InfisicalDynamicSecretReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the InfisicalDynamicSecret object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/reconcile +func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = logf.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *InfisicalDynamicSecretReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&secretsv1alpha1.InfisicalDynamicSecret{}). + Named("infisicaldynamicsecret"). + Complete(r) +} diff --git a/k8-operator/k8-operator/internal/controller/infisicaldynamicsecret_controller_test.go b/k8-operator/k8-operator/internal/controller/infisicaldynamicsecret_controller_test.go new file mode 100644 index 000000000..11b944205 --- /dev/null +++ b/k8-operator/k8-operator/internal/controller/infisicaldynamicsecret_controller_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" +) + +var _ = Describe("InfisicalDynamicSecret Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + infisicaldynamicsecret := &secretsv1alpha1.InfisicalDynamicSecret{} + + BeforeEach(func() { + By("creating the custom resource for the Kind InfisicalDynamicSecret") + err := k8sClient.Get(ctx, typeNamespacedName, infisicaldynamicsecret) + if err != nil && errors.IsNotFound(err) { + resource := &secretsv1alpha1.InfisicalDynamicSecret{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &secretsv1alpha1.InfisicalDynamicSecret{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance InfisicalDynamicSecret") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &InfisicalDynamicSecretReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/k8-operator/k8-operator/internal/controller/infisicalpushsecretsecret_controller.go b/k8-operator/k8-operator/internal/controller/infisicalpushsecretsecret_controller.go new file mode 100644 index 000000000..467865e79 --- /dev/null +++ b/k8-operator/k8-operator/internal/controller/infisicalpushsecretsecret_controller.go @@ -0,0 +1,63 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + logf "sigs.k8s.io/controller-runtime/pkg/log" + + secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" +) + +// InfisicalPushSecretSecretReconciler reconciles a InfisicalPushSecretSecret object +type InfisicalPushSecretSecretReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalpushsecretsecrets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalpushsecretsecrets/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalpushsecretsecrets/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the InfisicalPushSecretSecret object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/reconcile +func (r *InfisicalPushSecretSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = logf.FromContext(ctx) + + // TODO(user): your logic here + + return ctrl.Result{}, nil +} + +// SetupWithManager sets up the controller with the Manager. +func (r *InfisicalPushSecretSecretReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&secretsv1alpha1.InfisicalPushSecretSecret{}). + Named("infisicalpushsecretsecret"). + Complete(r) +} diff --git a/k8-operator/k8-operator/internal/controller/infisicalpushsecretsecret_controller_test.go b/k8-operator/k8-operator/internal/controller/infisicalpushsecretsecret_controller_test.go new file mode 100644 index 000000000..aca7994d8 --- /dev/null +++ b/k8-operator/k8-operator/internal/controller/infisicalpushsecretsecret_controller_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" +) + +var _ = Describe("InfisicalPushSecretSecret Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + infisicalpushsecretsecret := &secretsv1alpha1.InfisicalPushSecretSecret{} + + BeforeEach(func() { + By("creating the custom resource for the Kind InfisicalPushSecretSecret") + err := k8sClient.Get(ctx, typeNamespacedName, infisicalpushsecretsecret) + if err != nil && errors.IsNotFound(err) { + resource := &secretsv1alpha1.InfisicalPushSecretSecret{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &secretsv1alpha1.InfisicalPushSecretSecret{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance InfisicalPushSecretSecret") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &InfisicalPushSecretSecretReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/k8-operator/k8-operator/internal/controller/infisicalsecret_controller.go b/k8-operator/k8-operator/internal/controller/infisicalsecret_controller.go new file mode 100644 index 000000000..13d98243d --- /dev/null +++ b/k8-operator/k8-operator/internal/controller/infisicalsecret_controller.go @@ -0,0 +1,224 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "fmt" + "time" + + defaultErrors "errors" + + infisicalsecret "github.com/Infisical/infisical/k8-operator/internal/services/infisicalsecret" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" + + secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + "github.com/Infisical/infisical/k8-operator/internal/controllerhelpers" + "github.com/Infisical/infisical/k8-operator/internal/util" + "github.com/go-logr/logr" +) + +// InfisicalSecretReconciler reconciles a InfisicalSecret object +type InfisicalSecretReconciler struct { + client.Client + BaseLogger logr.Logger + Scheme *runtime.Scheme +} + +var infisicalSecretResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables) + +func (r *InfisicalSecretReconciler) GetLogger(req ctrl.Request) logr.Logger { + return r.BaseLogger.WithValues("infisicalsecret", req.NamespacedName) +} + +// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/finalizers,verbs=update + +// Reconcile is part of the main kubernetes reconciliation loop which aims to +// move the current state of the cluster closer to the desired state. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the InfisicalSecret object against the actual cluster state, and then +// perform operations to make the cluster state reflect the state specified by +// the user. +// +// For more details, check Reconcile and its Result here: +// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/reconcile +func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + logger := r.GetLogger(req) + + var infisicalSecretCRD secretsv1alpha1.InfisicalSecret + requeueTime := time.Minute // seconds + + err := r.Get(ctx, req.NamespacedName, &infisicalSecretCRD) + if err != nil { + if errors.IsNotFound(err) { + return ctrl.Result{ + Requeue: false, + }, nil + } else { + logger.Error(err, "unable to fetch Infisical Secret CRD from cluster") + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil + } + } + + // It's important we don't directly modify the CRD object, so we create a copy of it and move existing data into it. + managedKubeSecretReferences := infisicalSecretCRD.Spec.ManagedKubeSecretReferences + managedKubeConfigMapReferences := infisicalSecretCRD.Spec.ManagedKubeConfigMapReferences + + if infisicalSecretCRD.Spec.ManagedSecretReference.SecretName != "" && managedKubeSecretReferences != nil && len(managedKubeSecretReferences) > 0 { + errMessage := "InfisicalSecret CRD cannot have both managedSecretReference and managedKubeSecretReferences" + logger.Error(defaultErrors.New(errMessage), errMessage) + return ctrl.Result{}, defaultErrors.New(errMessage) + } + + if infisicalSecretCRD.Spec.ManagedSecretReference.SecretName != "" { + logger.Info("\n\n\nThe field `managedSecretReference` will be deprecated in the near future, please use `managedKubeSecretReferences` instead.\n\nRefer to the documentation for more information: https://infisical.com/docs/integrations/platforms/kubernetes/infisical-secret-crd\n\n\n") + + if managedKubeSecretReferences == nil { + managedKubeSecretReferences = []secretsv1alpha1.ManagedKubeSecretConfig{} + } + managedKubeSecretReferences = append(managedKubeSecretReferences, infisicalSecretCRD.Spec.ManagedSecretReference) + } + + if len(managedKubeSecretReferences) == 0 && len(managedKubeConfigMapReferences) == 0 { + errMessage := "InfisicalSecret CRD must have at least one managed secret reference set in the `managedKubeSecretReferences` or `managedKubeConfigMapReferences` field" + logger.Error(defaultErrors.New(errMessage), errMessage) + return ctrl.Result{}, defaultErrors.New(errMessage) + } + + // Remove finalizers if they exist. This is to support previous InfisicalSecret CRD's that have finalizers on them. + // In order to delete secrets with finalizers, we first remove the finalizers so we can use the simplified and improved deletion process + if !infisicalSecretCRD.ObjectMeta.DeletionTimestamp.IsZero() && len(infisicalSecretCRD.ObjectMeta.Finalizers) > 0 { + infisicalSecretCRD.ObjectMeta.Finalizers = []string{} + if err := r.Update(ctx, &infisicalSecretCRD); err != nil { + logger.Error(err, fmt.Sprintf("Error removing finalizers from Infisical Secret %s", infisicalSecretCRD.Name)) + return ctrl.Result{}, err + } + // Our finalizers have been removed, so the reconciler can do nothing. + return ctrl.Result{}, nil + } + + if infisicalSecretCRD.Spec.ResyncInterval != 0 { + requeueTime = time.Second * time.Duration(infisicalSecretCRD.Spec.ResyncInterval) + logger.Info(fmt.Sprintf("Manual re-sync interval set. Interval: %v", requeueTime)) + + } else { + logger.Info(fmt.Sprintf("Re-sync interval set. Interval: %v", requeueTime)) + } + + // Check if the resource is already marked for deletion + if infisicalSecretCRD.GetDeletionTimestamp() != nil { + return ctrl.Result{ + Requeue: false, + }, nil + } + + // Get modified/default config + infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client) + if err != nil { + logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime)) + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil + } + + // Initialize the business logic handler + businessLogic := infisicalsecret.NewInfisicalSecretHandler(r.Client, r.Scheme) + + // Setup API configuration through business logic + err = businessLogic.SetupAPIConfig(infisicalSecretCRD, infisicalConfig) + if err != nil { + logger.Error(err, fmt.Sprintf("unable to setup API configuration. Will requeue after [requeueTime=%v]", requeueTime)) + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil + } + + // Handle CA certificate through business logic + err = businessLogic.HandleCACertificate(ctx, infisicalSecretCRD) + if err != nil { + logger.Error(err, fmt.Sprintf("unable to handle CA certificate. Will requeue after [requeueTime=%v]", requeueTime)) + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil + } + + secretsCount, err := businessLogic.ReconcileInfisicalSecret(ctx, logger, &infisicalSecretCRD, managedKubeSecretReferences, managedKubeConfigMapReferences, infisicalSecretResourceVariablesMap) + businessLogic.SetReadyToSyncSecretsConditions(ctx, logger, &infisicalSecretCRD, secretsCount, err) + + if err != nil { + logger.Error(err, fmt.Sprintf("unable to reconcile InfisicalSecret. Will requeue after [requeueTime=%v]", requeueTime)) + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil + } + + numDeployments, err := controllerhelpers.ReconcileDeploymentsWithMultipleManagedSecrets(ctx, r.Client, logger, managedKubeSecretReferences) + businessLogic.SetInfisicalAutoRedeploymentReady(ctx, logger, &infisicalSecretCRD, numDeployments, err) + + if err != nil { + logger.Error(err, fmt.Sprintf("unable to reconcile auto redeployment. Will requeue after [requeueTime=%v]", requeueTime)) + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil + } + + // Sync again after the specified time + logger.Info(fmt.Sprintf("Successfully synced %d secrets. Operator will requeue after [%v]", secretsCount, requeueTime)) + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil +} + +func (r *InfisicalSecretReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&secretsv1alpha1.InfisicalSecret{}, builder.WithPredicates(predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { + if e.ObjectOld.GetGeneration() == e.ObjectNew.GetGeneration() { + return false // Skip reconciliation for status-only changes + } + + if infisicalSecretResourceVariablesMap != nil { + if rv, ok := infisicalSecretResourceVariablesMap[string(e.ObjectNew.GetUID())]; ok { + rv.CancelCtx() + delete(infisicalSecretResourceVariablesMap, string(e.ObjectNew.GetUID())) + } + } + return true + }, + DeleteFunc: func(e event.DeleteEvent) bool { + if infisicalSecretResourceVariablesMap != nil { + if rv, ok := infisicalSecretResourceVariablesMap[string(e.Object.GetUID())]; ok { + rv.CancelCtx() + delete(infisicalSecretResourceVariablesMap, string(e.Object.GetUID())) + } + } + return true + }, + })). + Complete(r) +} diff --git a/k8-operator/k8-operator/internal/controller/infisicalsecret_controller_test.go b/k8-operator/k8-operator/internal/controller/infisicalsecret_controller_test.go new file mode 100644 index 000000000..dd45e584d --- /dev/null +++ b/k8-operator/k8-operator/internal/controller/infisicalsecret_controller_test.go @@ -0,0 +1,84 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" +) + +var _ = Describe("InfisicalSecret Controller", func() { + Context("When reconciling a resource", func() { + const resourceName = "test-resource" + + ctx := context.Background() + + typeNamespacedName := types.NamespacedName{ + Name: resourceName, + Namespace: "default", // TODO(user):Modify as needed + } + infisicalsecret := &secretsv1alpha1.InfisicalSecret{} + + BeforeEach(func() { + By("creating the custom resource for the Kind InfisicalSecret") + err := k8sClient.Get(ctx, typeNamespacedName, infisicalsecret) + if err != nil && errors.IsNotFound(err) { + resource := &secretsv1alpha1.InfisicalSecret{ + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: "default", + }, + // TODO(user): Specify other spec details if needed. + } + Expect(k8sClient.Create(ctx, resource)).To(Succeed()) + } + }) + + AfterEach(func() { + // TODO(user): Cleanup logic after each test, like removing the resource instance. + resource := &secretsv1alpha1.InfisicalSecret{} + err := k8sClient.Get(ctx, typeNamespacedName, resource) + Expect(err).NotTo(HaveOccurred()) + + By("Cleanup the specific resource instance InfisicalSecret") + Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) + }) + It("should successfully reconcile the resource", func() { + By("Reconciling the created resource") + controllerReconciler := &InfisicalSecretReconciler{ + Client: k8sClient, + Scheme: k8sClient.Scheme(), + } + + _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ + NamespacedName: typeNamespacedName, + }) + Expect(err).NotTo(HaveOccurred()) + // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. + // Example: If you expect a certain status condition after reconciliation, verify it here. + }) + }) +}) diff --git a/k8-operator/k8-operator/internal/controller/suite_test.go b/k8-operator/k8-operator/internal/controller/suite_test.go new file mode 100644 index 000000000..d1459e9aa --- /dev/null +++ b/k8-operator/k8-operator/internal/controller/suite_test.go @@ -0,0 +1,116 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "os" + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + // +kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var ( + ctx context.Context + cancel context.CancelFunc + testEnv *envtest.Environment + cfg *rest.Config + k8sClient client.Client +) + +func TestControllers(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Controller Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + ctx, cancel = context.WithCancel(context.TODO()) + + var err error + err = secretsv1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + // +kubebuilder:scaffold:scheme + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: true, + } + + // Retrieve the first found binary directory to allow running tests from IDEs + if getFirstFoundEnvTestBinaryDir() != "" { + testEnv.BinaryAssetsDirectory = getFirstFoundEnvTestBinaryDir() + } + + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + cancel() + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) + +// getFirstFoundEnvTestBinaryDir locates the first binary in the specified path. +// ENVTEST-based tests depend on specific binaries, usually located in paths set by +// controller-runtime. When running tests directly (e.g., via an IDE) without using +// Makefile targets, the 'BinaryAssetsDirectory' must be explicitly configured. +// +// This function streamlines the process by finding the required binaries, similar to +// setting the 'KUBEBUILDER_ASSETS' environment variable. To ensure the binaries are +// properly set up, run 'make setup-envtest' beforehand. +func getFirstFoundEnvTestBinaryDir() string { + basePath := filepath.Join("..", "..", "bin", "k8s") + entries, err := os.ReadDir(basePath) + if err != nil { + logf.Log.Error(err, "Failed to read directory", "path", basePath) + return "" + } + for _, entry := range entries { + if entry.IsDir() { + return filepath.Join(basePath, entry.Name()) + } + } + return "" +} diff --git a/k8-operator/k8-operator/internal/controllerhelpers/controllerhelpers.go b/k8-operator/k8-operator/internal/controllerhelpers/controllerhelpers.go new file mode 100644 index 000000000..0149d90a0 --- /dev/null +++ b/k8-operator/k8-operator/internal/controllerhelpers/controllerhelpers.go @@ -0,0 +1,293 @@ +package controllerhelpers + +import ( + "context" + "fmt" + "sync" + + "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + "github.com/Infisical/infisical/k8-operator/internal/constants" + "github.com/go-logr/logr" + v1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + k8Errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + controllerClient "sigs.k8s.io/controller-runtime/pkg/client" +) + +const DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX = "secrets.infisical.com/managed-secret" +const AUTO_RELOAD_DEPLOYMENT_ANNOTATION = "secrets.infisical.com/auto-reload" // needs to be set to true for a deployment to start auto redeploying + +func ReconcileDeploymentsWithManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecret v1alpha1.ManagedKubeSecretConfig) (int, error) { + listOfDeployments := &v1.DeploymentList{} + + err := client.List(ctx, listOfDeployments, &controllerClient.ListOptions{Namespace: managedSecret.SecretNamespace}) + if err != nil { + return 0, fmt.Errorf("unable to get deployments in the [namespace=%v] [err=%v]", managedSecret.SecretNamespace, err) + } + + listOfDaemonSets := &v1.DaemonSetList{} + err = client.List(ctx, listOfDaemonSets, &controllerClient.ListOptions{Namespace: managedSecret.SecretNamespace}) + if err != nil { + return 0, fmt.Errorf("unable to get daemonSets in the [namespace=%v] [err=%v]", managedSecret.SecretNamespace, err) + } + + listOfStatefulSets := &v1.StatefulSetList{} + err = client.List(ctx, listOfStatefulSets, &controllerClient.ListOptions{Namespace: managedSecret.SecretNamespace}) + if err != nil { + return 0, fmt.Errorf("unable to get statefulSets in the [namespace=%v] [err=%v]", managedSecret.SecretNamespace, err) + } + + managedKubeSecretNameAndNamespace := types.NamespacedName{ + Namespace: managedSecret.SecretNamespace, + Name: managedSecret.SecretName, + } + + managedKubeSecret := &corev1.Secret{} + err = client.Get(ctx, managedKubeSecretNameAndNamespace, managedKubeSecret) + if err != nil { + return 0, fmt.Errorf("unable to fetch Kubernetes secret to update deployment: %v", err) + } + + var wg sync.WaitGroup + + // Iterate over the deployments and check if they use the managed secret + for _, deployment := range listOfDeployments.Items { + deployment := deployment + if deployment.Annotations[AUTO_RELOAD_DEPLOYMENT_ANNOTATION] == "true" && IsDeploymentUsingManagedSecret(deployment, managedSecret) { + // Start a goroutine to reconcile the deployment + wg.Add(1) + go func(deployment v1.Deployment, managedSecret corev1.Secret) { + defer wg.Done() + if err := ReconcileDeployment(ctx, client, logger, deployment, managedSecret); err != nil { + logger.Error(err, fmt.Sprintf("unable to reconcile deployment with [name=%v]. Will try next requeue", deployment.ObjectMeta.Name)) + } + }(deployment, *managedKubeSecret) + } + } + + // Iterate over the daemonSets and check if they use the managed secret + for _, daemonSet := range listOfDaemonSets.Items { + daemonSet := daemonSet + if daemonSet.Annotations[AUTO_RELOAD_DEPLOYMENT_ANNOTATION] == "true" && IsDaemonSetUsingManagedSecret(daemonSet, managedSecret) { + wg.Add(1) + go func(deployment v1.DaemonSet, managedSecret corev1.Secret) { + defer wg.Done() + if err := ReconcileDaemonSet(ctx, client, logger, daemonSet, managedSecret); err != nil { + logger.Error(err, fmt.Sprintf("unable to reconcile daemonset with [name=%v]. Will try next requeue", deployment.ObjectMeta.Name)) + } + }(daemonSet, *managedKubeSecret) + } + } + + // Iterate over the statefulSets and check if they use the managed secret + for _, statefulSet := range listOfStatefulSets.Items { + statefulSet := statefulSet + if statefulSet.Annotations[AUTO_RELOAD_DEPLOYMENT_ANNOTATION] == "true" && IsStatefulSetUsingManagedSecret(statefulSet, managedSecret) { + wg.Add(1) + go func(statefulSet v1.StatefulSet, managedSecret corev1.Secret) { + defer wg.Done() + if err := ReconcileStatefulSet(ctx, client, logger, statefulSet, managedSecret); err != nil { + logger.Error(err, fmt.Sprintf("unable to reconcile statefulset with [name=%v]. Will try next requeue", statefulSet.ObjectMeta.Name)) + } + }(statefulSet, *managedKubeSecret) + } + } + + wg.Wait() + + return 0, nil +} + +func ReconcileDeploymentsWithMultipleManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecrets []v1alpha1.ManagedKubeSecretConfig) (int, error) { + for _, managedSecret := range managedSecrets { + _, err := ReconcileDeploymentsWithManagedSecrets(ctx, client, logger, managedSecret) + if err != nil { + logger.Error(err, fmt.Sprintf("unable to reconcile deployments with managed secret [name=%v]", managedSecret.SecretName)) + return 0, err + } + } + return 0, nil +} + +// Check if the deployment uses managed secrets +func IsDeploymentUsingManagedSecret(deployment v1.Deployment, managedSecret v1alpha1.ManagedKubeSecretConfig) bool { + managedSecretName := managedSecret.SecretName + for _, container := range deployment.Spec.Template.Spec.Containers { + for _, envFrom := range container.EnvFrom { + if envFrom.SecretRef != nil && envFrom.SecretRef.LocalObjectReference.Name == managedSecretName { + return true + } + } + for _, env := range container.Env { + if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.LocalObjectReference.Name == managedSecretName { + return true + } + } + } + for _, volume := range deployment.Spec.Template.Spec.Volumes { + if volume.Secret != nil && volume.Secret.SecretName == managedSecretName { + return true + } + } + + return false +} + +func IsDaemonSetUsingManagedSecret(daemonSet v1.DaemonSet, managedSecret v1alpha1.ManagedKubeSecretConfig) bool { + managedSecretName := managedSecret.SecretName + for _, container := range daemonSet.Spec.Template.Spec.Containers { + for _, envFrom := range container.EnvFrom { + if envFrom.SecretRef != nil && envFrom.SecretRef.LocalObjectReference.Name == managedSecretName { + return true + } + } + for _, env := range container.Env { + if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.LocalObjectReference.Name == managedSecretName { + return true + } + } + } + + for _, volume := range daemonSet.Spec.Template.Spec.Volumes { + if volume.Secret != nil && volume.Secret.SecretName == managedSecretName { + return true + } + } + + return false +} + +func IsStatefulSetUsingManagedSecret(statefulSet v1.StatefulSet, managedSecret v1alpha1.ManagedKubeSecretConfig) bool { + managedSecretName := managedSecret.SecretName + for _, container := range statefulSet.Spec.Template.Spec.Containers { + for _, envFrom := range container.EnvFrom { + if envFrom.SecretRef != nil && envFrom.SecretRef.LocalObjectReference.Name == managedSecretName { + return true + } + } + for _, env := range container.Env { + if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.LocalObjectReference.Name == managedSecretName { + return true + } + } + } + for _, volume := range statefulSet.Spec.Template.Spec.Volumes { + if volume.Secret != nil && volume.Secret.SecretName == managedSecretName { + return true + } + } + + return false +} + +// This function ensures that a deployment is in sync with a Kubernetes secret by comparing their versions. +// If the version of the secret is different from the version annotation on the deployment, the annotation is updated to trigger a restart of the deployment. +func ReconcileDeployment(ctx context.Context, client controllerClient.Client, logger logr.Logger, deployment v1.Deployment, secret corev1.Secret) error { + annotationKey := fmt.Sprintf("%s.%s", DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX, secret.Name) + annotationValue := secret.Annotations[constants.SECRET_VERSION_ANNOTATION] + + if deployment.Annotations[annotationKey] == annotationValue && + deployment.Spec.Template.Annotations[annotationKey] == annotationValue { + logger.Info(fmt.Sprintf("The [deploymentName=%v] is already using the most up to date managed secrets. No action required.", deployment.ObjectMeta.Name)) + return nil + } + + logger.Info(fmt.Sprintf("Deployment is using outdated managed secret. Starting re-deployment [deploymentName=%v]", deployment.ObjectMeta.Name)) + + if deployment.Spec.Template.Annotations == nil { + deployment.Spec.Template.Annotations = make(map[string]string) + } + + deployment.Annotations[annotationKey] = annotationValue + deployment.Spec.Template.Annotations[annotationKey] = annotationValue + + if err := client.Update(ctx, &deployment); err != nil { + return fmt.Errorf("failed to update deployment annotation: %v", err) + } + return nil +} + +func ReconcileDaemonSet(ctx context.Context, client controllerClient.Client, logger logr.Logger, daemonSet v1.DaemonSet, secret corev1.Secret) error { + annotationKey := fmt.Sprintf("%s.%s", DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX, secret.Name) + annotationValue := secret.Annotations[constants.SECRET_VERSION_ANNOTATION] + + if daemonSet.Annotations[annotationKey] == annotationValue && + daemonSet.Spec.Template.Annotations[annotationKey] == annotationValue { + logger.Info(fmt.Sprintf("The [daemonSetName=%v] is already using the most up to date managed secrets. No action required.", daemonSet.ObjectMeta.Name)) + return nil + } + + logger.Info(fmt.Sprintf("DaemonSet is using outdated managed secret. Starting re-deployment [daemonSetName=%v]", daemonSet.ObjectMeta.Name)) + + if daemonSet.Spec.Template.Annotations == nil { + daemonSet.Spec.Template.Annotations = make(map[string]string) + } + + daemonSet.Annotations[annotationKey] = annotationValue + daemonSet.Spec.Template.Annotations[annotationKey] = annotationValue + + if err := client.Update(ctx, &daemonSet); err != nil { + return fmt.Errorf("failed to update daemonSet annotation: %v", err) + } + return nil +} + +func ReconcileStatefulSet(ctx context.Context, client controllerClient.Client, logger logr.Logger, statefulSet v1.StatefulSet, secret corev1.Secret) error { + annotationKey := fmt.Sprintf("%s.%s", DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX, secret.Name) + annotationValue := secret.Annotations[constants.SECRET_VERSION_ANNOTATION] + + if statefulSet.Annotations[annotationKey] == annotationValue && + statefulSet.Spec.Template.Annotations[annotationKey] == annotationValue { + logger.Info(fmt.Sprintf("The [statefulSetName=%v] is already using the most up to date managed secrets. No action required.", statefulSet.ObjectMeta.Name)) + return nil + } + + logger.Info(fmt.Sprintf("StatefulSet is using outdated managed secret. Starting re-deployment [statefulSetName=%v]", statefulSet.ObjectMeta.Name)) + + if statefulSet.Spec.Template.Annotations == nil { + statefulSet.Spec.Template.Annotations = make(map[string]string) + } + + statefulSet.Annotations[annotationKey] = annotationValue + statefulSet.Spec.Template.Annotations[annotationKey] = annotationValue + + if err := client.Update(ctx, &statefulSet); err != nil { + return fmt.Errorf("failed to update statefulSet annotation: %v", err) + } + return nil +} + +func GetInfisicalConfigMap(ctx context.Context, client client.Client) (configMap map[string]string, errToReturn error) { + // default key values + defaultConfigMapData := make(map[string]string) + defaultConfigMapData["hostAPI"] = constants.INFISICAL_DOMAIN + + kubeConfigMap := &corev1.ConfigMap{} + err := client.Get(ctx, types.NamespacedName{ + Namespace: constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, + Name: constants.OPERATOR_SETTINGS_CONFIGMAP_NAME, + }, kubeConfigMap) + + if err != nil { + if k8Errors.IsNotFound(err) { + kubeConfigMap = nil + } else { + return nil, fmt.Errorf("GetConfigMapByNamespacedName: unable to fetch config map in [namespacedName=%s] [err=%s]", constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, err) + } + } + + if kubeConfigMap == nil { + return defaultConfigMapData, nil + } else { + for key, value := range defaultConfigMapData { + _, exists := kubeConfigMap.Data[key] + if !exists { + kubeConfigMap.Data[key] = value + } + } + + return kubeConfigMap.Data, nil + } +} diff --git a/k8-operator/k8-operator/internal/controllerutil/util.go b/k8-operator/k8-operator/internal/controllerutil/util.go new file mode 100644 index 000000000..67e0cdbe4 --- /dev/null +++ b/k8-operator/k8-operator/internal/controllerutil/util.go @@ -0,0 +1,45 @@ +package controllerhelpers + +import ( + "context" + "fmt" + + "github.com/Infisical/infisical/k8-operator/internal/constants" + corev1 "k8s.io/api/core/v1" + k8Errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func GetInfisicalConfigMap(ctx context.Context, client client.Client) (configMap map[string]string, errToReturn error) { + // default key values + defaultConfigMapData := make(map[string]string) + defaultConfigMapData["hostAPI"] = constants.INFISICAL_DOMAIN + + kubeConfigMap := &corev1.ConfigMap{} + err := client.Get(ctx, types.NamespacedName{ + Namespace: constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, + Name: constants.OPERATOR_SETTINGS_CONFIGMAP_NAME, + }, kubeConfigMap) + + if err != nil { + if k8Errors.IsNotFound(err) { + kubeConfigMap = nil + } else { + return nil, fmt.Errorf("GetConfigMapByNamespacedName: unable to fetch config map in [namespacedName=%s] [err=%s]", constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, err) + } + } + + if kubeConfigMap == nil { + return defaultConfigMapData, nil + } else { + for key, value := range defaultConfigMapData { + _, exists := kubeConfigMap.Data[key] + if !exists { + kubeConfigMap.Data[key] = value + } + } + + return kubeConfigMap.Data, nil + } +} diff --git a/k8-operator/k8-operator/internal/crypto/crypto.go b/k8-operator/k8-operator/internal/crypto/crypto.go new file mode 100644 index 000000000..810382af1 --- /dev/null +++ b/k8-operator/k8-operator/internal/crypto/crypto.go @@ -0,0 +1,42 @@ +package crypto + +import ( + "crypto/aes" + "crypto/cipher" + "fmt" + "hash/crc32" + + "golang.org/x/crypto/nacl/box" +) + +func DecryptSymmetric(key []byte, encryptedPrivateKey []byte, tag []byte, IV []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + aesgcm, err := cipher.NewGCMWithNonceSize(block, len(IV)) + if err != nil { + return nil, err + } + + var nonce = IV + var ciphertext = append(encryptedPrivateKey, tag...) + + plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, err + } + + return plaintext, nil +} + +func DecryptAsymmetric(ciphertext []byte, nonce []byte, publicKey []byte, privateKey []byte) (plainText []byte) { + plainTextToReturn, _ := box.Open(nil, ciphertext, (*[24]byte)(nonce), (*[32]byte)(publicKey), (*[32]byte)(privateKey)) + return plainTextToReturn +} + +func ComputeEtag(data []byte) string { + crc := crc32.ChecksumIEEE(data) + return fmt.Sprintf(`W/"secrets-%d-%08X"`, len(data), crc) +} diff --git a/k8-operator/k8-operator/internal/generator/generator.go b/k8-operator/k8-operator/internal/generator/generator.go new file mode 100644 index 000000000..cc1b290c7 --- /dev/null +++ b/k8-operator/k8-operator/internal/generator/generator.go @@ -0,0 +1 @@ +package generator diff --git a/k8-operator/k8-operator/internal/generator/password.go b/k8-operator/k8-operator/internal/generator/password.go new file mode 100644 index 000000000..d322f1014 --- /dev/null +++ b/k8-operator/k8-operator/internal/generator/password.go @@ -0,0 +1,76 @@ +package generator + +import ( + "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + "github.com/sethvargo/go-password/password" +) + +const ( + defaultLength = 24 + defaultSymbolChars = "~!@#$%^&*()_+`-={}|[]\\:\"<>?,./" + digitFactor = 0.25 + symbolFactor = 0.25 +) + +func generateSafePassword( + passLen int, + symbols int, + symbolCharacters string, + digits int, + noUpper bool, + allowRepeat bool, +) (string, error) { + gen, err := password.NewGenerator(&password.GeneratorInput{ + Symbols: symbolCharacters, + }) + if err != nil { + return "", err + } + return gen.Generate( + passLen, + digits, + symbols, + noUpper, + allowRepeat, + ) +} + +func GeneratorPassword(spec v1alpha1.PasswordSpec) (string, error) { + + symbolCharacters := defaultSymbolChars + + if spec.SymbolCharacters != nil && *spec.SymbolCharacters != "" { + symbolCharacters = *spec.SymbolCharacters + } + + passwordLength := defaultLength + + if spec.Length != 0 { + passwordLength = spec.Length + } + + digits := int(float32(passwordLength) * digitFactor) + if spec.Digits != nil { + digits = *spec.Digits + } + + symbols := int(float32(passwordLength) * symbolFactor) + if spec.Symbols != nil { + symbols = *spec.Symbols + } + + pass, err := generateSafePassword( + passwordLength, + symbols, + symbolCharacters, + digits, + spec.NoUpper, + spec.AllowRepeat, + ) + + if err != nil { + return "", err + } + + return pass, nil +} diff --git a/k8-operator/k8-operator/internal/generator/uuid.go b/k8-operator/k8-operator/internal/generator/uuid.go new file mode 100644 index 000000000..b9249f783 --- /dev/null +++ b/k8-operator/k8-operator/internal/generator/uuid.go @@ -0,0 +1,10 @@ +package generator + +import ( + "github.com/google/uuid" +) + +func GeneratorUUID() (string, error) { + uuid := uuid.New().String() + return uuid, nil +} diff --git a/k8-operator/k8-operator/internal/model/model.go b/k8-operator/k8-operator/internal/model/model.go new file mode 100644 index 000000000..3b3bc3216 --- /dev/null +++ b/k8-operator/k8-operator/internal/model/model.go @@ -0,0 +1,37 @@ +package model + +type ServiceAccountDetails struct { + AccessKey string + PublicKey string + PrivateKey string +} + +type MachineIdentityDetails struct { + ClientId string + ClientSecret string +} + +type SingleEnvironmentVariable struct { + Key string `json:"key"` + Value string `json:"value"` + SecretPath string `json:"secretPath"` + Type string `json:"type"` + ID string `json:"id"` +} + +type SecretTemplateOptions struct { + Value string `json:"value"` + SecretPath string `json:"secretPath"` +} + +type Project struct { + ID string `json:"id"` + Name string `json:"name"` + Slug string `json:"slug"` + OrgID string `json:"orgId"` + Environments []struct { + Name string `json:"name"` + Slug string `json:"slug"` + ID string `json:"id"` + } +} diff --git a/k8-operator/k8-operator/internal/services/infisicalsecret/conditions.go b/k8-operator/k8-operator/internal/services/infisicalsecret/conditions.go new file mode 100644 index 000000000..9feb57383 --- /dev/null +++ b/k8-operator/k8-operator/internal/services/infisicalsecret/conditions.go @@ -0,0 +1,100 @@ +package infisicalsecret + +import ( + "context" + "fmt" + + "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + "github.com/Infisical/infisical/k8-operator/internal/util" + "github.com/go-logr/logr" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func (r *InfisicalSecretReconciler) SetReadyToSyncSecretsConditions(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, secretsCount int, errorToConditionOn error) { + if infisicalSecret.Status.Conditions == nil { + infisicalSecret.Status.Conditions = []metav1.Condition{} + } + + if errorToConditionOn != nil { + meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{ + Type: "secrets.infisical.com/ReadyToSyncSecrets", + Status: metav1.ConditionFalse, + Reason: "Error", + Message: fmt.Sprintf("Failed to sync secrets. This can be caused by invalid access token or an invalid API host that is set. Error: %v", errorToConditionOn), + }) + + meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{ + Type: "secrets.infisical.com/AutoRedeployReady", + Status: metav1.ConditionFalse, + Reason: "Stopped", + Message: fmt.Sprintf("Auto redeployment has been stopped because the operator failed to sync secrets. Error: %v", errorToConditionOn), + }) + } else { + meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{ + Type: "secrets.infisical.com/ReadyToSyncSecrets", + Status: metav1.ConditionTrue, + Reason: "OK", + Message: fmt.Sprintf("Infisical controller has started syncing your secrets. Last reconcile synced %d secrets", secretsCount), + }) + } + + err := r.Client.Status().Update(ctx, infisicalSecret) + if err != nil { + logger.Error(err, "Could not set condition for ReadyToSyncSecrets") + } +} + +func (r *InfisicalSecretReconciler) SetInfisicalTokenLoadCondition(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, authStrategy util.AuthStrategyType, errorToConditionOn error) { + if infisicalSecret.Status.Conditions == nil { + infisicalSecret.Status.Conditions = []metav1.Condition{} + } + + if errorToConditionOn == nil { + meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{ + Type: "secrets.infisical.com/LoadedInfisicalToken", + Status: metav1.ConditionTrue, + Reason: "OK", + Message: fmt.Sprintf("Infisical controller has loaded the Infisical token in provided Kubernetes secret, using %v authentication strategy", authStrategy), + }) + } else { + meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{ + Type: "secrets.infisical.com/LoadedInfisicalToken", + Status: metav1.ConditionFalse, + Reason: "Error", + Message: fmt.Sprintf("Failed to load Infisical Token from the provided Kubernetes secret because: %v", errorToConditionOn), + }) + } + + err := r.Client.Status().Update(ctx, infisicalSecret) + if err != nil { + logger.Error(err, "Could not set condition for LoadedInfisicalToken") + } +} + +func (r *InfisicalSecretReconciler) SetInfisicalAutoRedeploymentReady(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, numDeployments int, errorToConditionOn error) { + if infisicalSecret.Status.Conditions == nil { + infisicalSecret.Status.Conditions = []metav1.Condition{} + } + + if errorToConditionOn == nil { + meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{ + Type: "secrets.infisical.com/AutoRedeployReady", + Status: metav1.ConditionTrue, + Reason: "OK", + Message: fmt.Sprintf("Infisical has found %v deployments which are ready to be auto redeployed when secrets change", numDeployments), + }) + } else { + meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{ + Type: "secrets.infisical.com/AutoRedeployReady", + Status: metav1.ConditionFalse, + Reason: "Error", + Message: fmt.Sprintf("Failed reconcile deployments because: %v", errorToConditionOn), + }) + } + + err := r.Client.Status().Update(ctx, infisicalSecret) + if err != nil { + logger.Error(err, "Could not set condition for AutoRedeployReady") + } +} diff --git a/k8-operator/k8-operator/internal/services/infisicalsecret/handler.go b/k8-operator/k8-operator/internal/services/infisicalsecret/handler.go new file mode 100644 index 000000000..ec3b50671 --- /dev/null +++ b/k8-operator/k8-operator/internal/services/infisicalsecret/handler.go @@ -0,0 +1,94 @@ +package infisicalsecret + +import ( + "context" + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + "github.com/Infisical/infisical/k8-operator/internal/api" + "github.com/Infisical/infisical/k8-operator/internal/util" + "github.com/go-logr/logr" + k8Errors "k8s.io/apimachinery/pkg/api/errors" +) + +type InfisicalSecretHandler struct { + client.Client + Scheme *runtime.Scheme +} + +func NewInfisicalSecretHandler(client client.Client, scheme *runtime.Scheme) *InfisicalSecretHandler { + return &InfisicalSecretHandler{ + Client: client, + Scheme: scheme, + } +} + +func (h *InfisicalSecretHandler) SetupAPIConfig(infisicalSecret v1alpha1.InfisicalSecret, infisicalConfig map[string]string) error { + if infisicalSecret.Spec.HostAPI == "" { + api.API_HOST_URL = infisicalConfig["hostAPI"] + } else { + api.API_HOST_URL = util.AppendAPIEndpoint(infisicalSecret.Spec.HostAPI) + } + return nil +} + +func (h *InfisicalSecretHandler) getInfisicalCaCertificateFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (caCertificate string, err error) { + + caCertificateFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, h.Client, types.NamespacedName{ + Namespace: infisicalSecret.Spec.TLS.CaRef.SecretNamespace, + Name: infisicalSecret.Spec.TLS.CaRef.SecretName, + }) + + if k8Errors.IsNotFound(err) { + return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err) + } + + if err != nil { + return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err) + } + + caCertificateFromSecret := string(caCertificateFromKubeSecret.Data[infisicalSecret.Spec.TLS.CaRef.SecretKey]) + + return caCertificateFromSecret, nil +} + +func (h *InfisicalSecretHandler) HandleCACertificate(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) error { + if infisicalSecret.Spec.TLS.CaRef.SecretName != "" { + caCert, err := h.getInfisicalCaCertificateFromKubeSecret(ctx, infisicalSecret) + if err != nil { + return err + } + api.API_CA_CERTIFICATE = caCert + } else { + api.API_CA_CERTIFICATE = "" + } + return nil +} + +func (h *InfisicalSecretHandler) ReconcileInfisicalSecret(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, managedKubeSecretReferences []v1alpha1.ManagedKubeSecretConfig, managedKubeConfigMapReferences []v1alpha1.ManagedKubeConfigMapConfig, resourceVariablesMap map[string]util.ResourceVariables) (int, error) { + reconciler := &InfisicalSecretReconciler{ + Client: h.Client, + Scheme: h.Scheme, + } + return reconciler.ReconcileInfisicalSecret(ctx, logger, infisicalSecret, managedKubeSecretReferences, managedKubeConfigMapReferences, resourceVariablesMap) +} + +func (h *InfisicalSecretHandler) SetReadyToSyncSecretsConditions(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, secretsCount int, errorToConditionOn error) { + reconciler := &InfisicalSecretReconciler{ + Client: h.Client, + Scheme: h.Scheme, + } + reconciler.SetReadyToSyncSecretsConditions(ctx, logger, infisicalSecret, secretsCount, errorToConditionOn) +} + +func (h *InfisicalSecretHandler) SetInfisicalAutoRedeploymentReady(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, numDeployments int, errorToConditionOn error) { + reconciler := &InfisicalSecretReconciler{ + Client: h.Client, + Scheme: h.Scheme, + } + reconciler.SetInfisicalAutoRedeploymentReady(ctx, logger, infisicalSecret, numDeployments, errorToConditionOn) +} diff --git a/k8-operator/k8-operator/internal/services/infisicalsecret/reconciler.go b/k8-operator/k8-operator/internal/services/infisicalsecret/reconciler.go new file mode 100644 index 000000000..df6c20428 --- /dev/null +++ b/k8-operator/k8-operator/internal/services/infisicalsecret/reconciler.go @@ -0,0 +1,577 @@ +package infisicalsecret + +import ( + "bytes" + "context" + "errors" + "fmt" + "strings" + tpl "text/template" + + "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + "github.com/Infisical/infisical/k8-operator/internal/api" + "github.com/Infisical/infisical/k8-operator/internal/constants" + "github.com/Infisical/infisical/k8-operator/internal/crypto" + "github.com/Infisical/infisical/k8-operator/internal/model" + "github.com/Infisical/infisical/k8-operator/internal/template" + "github.com/Infisical/infisical/k8-operator/internal/util" + "github.com/go-logr/logr" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + infisicalSdk "github.com/infisical/go-sdk" + corev1 "k8s.io/api/core/v1" + k8Errors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" +) + +const FINALIZER_NAME = "secrets.finalizers.infisical.com" + +type InfisicalSecretReconciler struct { + client.Client + Scheme *runtime.Scheme +} + +func (r *InfisicalSecretReconciler) handleAuthentication(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error) { + + // ? Legacy support, service token auth + infisicalToken, err := r.getInfisicalTokenFromKubeSecret(ctx, infisicalSecret) + if err != nil { + return util.AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get service token from kube secret [err=%s]", err) + } + if infisicalToken != "" { + infisicalClient.Auth().SetAccessToken(infisicalToken) + return util.AuthenticationDetails{AuthStrategy: util.AuthStrategy.SERVICE_TOKEN}, nil + } + + // ? Legacy support, service account auth + serviceAccountCreds, err := r.getInfisicalServiceAccountCredentialsFromKubeSecret(ctx, infisicalSecret) + if err != nil { + return util.AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get service account creds from kube secret [err=%s]", err) + } + + if serviceAccountCreds.AccessKey != "" || serviceAccountCreds.PrivateKey != "" || serviceAccountCreds.PublicKey != "" { + infisicalClient.Auth().SetAccessToken(serviceAccountCreds.AccessKey) + return util.AuthenticationDetails{AuthStrategy: util.AuthStrategy.SERVICE_ACCOUNT}, nil + } + + authStrategies := map[util.AuthStrategyType]func(ctx context.Context, reconcilerClient client.Client, secretCrd util.SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error){ + util.AuthStrategy.UNIVERSAL_MACHINE_IDENTITY: util.HandleUniversalAuth, + util.AuthStrategy.KUBERNETES_MACHINE_IDENTITY: util.HandleKubernetesAuth, + util.AuthStrategy.AWS_IAM_MACHINE_IDENTITY: util.HandleAwsIamAuth, + util.AuthStrategy.AZURE_MACHINE_IDENTITY: util.HandleAzureAuth, + util.AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: util.HandleGcpIdTokenAuth, + util.AuthStrategy.GCP_IAM_MACHINE_IDENTITY: util.HandleGcpIamAuth, + } + + for authStrategy, authHandler := range authStrategies { + authDetails, err := authHandler(ctx, r.Client, util.SecretAuthInput{ + Secret: infisicalSecret, + Type: util.SecretCrd.INFISICAL_SECRET, + }, infisicalClient) + + if err == nil { + return authDetails, nil + } + + if !errors.Is(err, util.ErrAuthNotApplicable) { + return util.AuthenticationDetails{}, fmt.Errorf("authentication failed for strategy [%s] [err=%w]", authStrategy, err) + } + } + + return util.AuthenticationDetails{}, fmt.Errorf("no authentication method provided") + +} + +func (r *InfisicalSecretReconciler) getInfisicalTokenFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (string, error) { + // default to new secret ref structure + secretName := infisicalSecret.Spec.Authentication.ServiceToken.ServiceTokenSecretReference.SecretName + secretNamespace := infisicalSecret.Spec.Authentication.ServiceToken.ServiceTokenSecretReference.SecretNamespace + // fall back to previous secret ref + if secretName == "" { + secretName = infisicalSecret.Spec.TokenSecretReference.SecretName + } + + if secretNamespace == "" { + secretNamespace = infisicalSecret.Spec.TokenSecretReference.SecretNamespace + } + + tokenSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{ + Namespace: secretNamespace, + Name: secretName, + }) + + if k8Errors.IsNotFound(err) { + return "", nil + } + + if err != nil { + return "", fmt.Errorf("failed to read Infisical token secret from secret named [%s] in namespace [%s]: with error [%w]", infisicalSecret.Spec.TokenSecretReference.SecretName, infisicalSecret.Spec.TokenSecretReference.SecretNamespace, err) + } + + infisicalServiceToken := tokenSecret.Data[constants.INFISICAL_TOKEN_SECRET_KEY_NAME] + + return strings.Replace(string(infisicalServiceToken), " ", "", -1), nil +} + +func (r *InfisicalSecretReconciler) getInfisicalCaCertificateFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (caCertificate string, err error) { + + caCertificateFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{ + Namespace: infisicalSecret.Spec.TLS.CaRef.SecretNamespace, + Name: infisicalSecret.Spec.TLS.CaRef.SecretName, + }) + + if k8Errors.IsNotFound(err) { + return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err) + } + + if err != nil { + return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err) + } + + caCertificateFromSecret := string(caCertificateFromKubeSecret.Data[infisicalSecret.Spec.TLS.CaRef.SecretKey]) + + return caCertificateFromSecret, nil +} + +// Fetches service account credentials from a Kubernetes secret specified in the infisicalSecret object, extracts the access key, public key, and private key from the secret, and returns them as a ServiceAccountCredentials object. +// If any keys are missing or an error occurs, returns an empty object or an error object, respectively. +func (r *InfisicalSecretReconciler) getInfisicalServiceAccountCredentialsFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (serviceAccountDetails model.ServiceAccountDetails, err error) { + serviceAccountCredsFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{ + Namespace: infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretNamespace, + Name: infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretName, + }) + + if k8Errors.IsNotFound(err) { + return model.ServiceAccountDetails{}, nil + } + + if err != nil { + return model.ServiceAccountDetails{}, fmt.Errorf("something went wrong when fetching your service account credentials [err=%s]", err) + } + + accessKeyFromSecret := serviceAccountCredsFromKubeSecret.Data[constants.SERVICE_ACCOUNT_ACCESS_KEY] + publicKeyFromSecret := serviceAccountCredsFromKubeSecret.Data[constants.SERVICE_ACCOUNT_PUBLIC_KEY] + privateKeyFromSecret := serviceAccountCredsFromKubeSecret.Data[constants.SERVICE_ACCOUNT_PRIVATE_KEY] + + if accessKeyFromSecret == nil || publicKeyFromSecret == nil || privateKeyFromSecret == nil { + return model.ServiceAccountDetails{}, nil + } + + return model.ServiceAccountDetails{AccessKey: string(accessKeyFromSecret), PrivateKey: string(privateKeyFromSecret), PublicKey: string(publicKeyFromSecret)}, nil +} + +func convertBinaryToStringMap(binaryMap map[string][]byte) map[string]string { + stringMap := make(map[string]string) + for k, v := range binaryMap { + stringMap[k] = string(v) + } + return stringMap +} + +func (r *InfisicalSecretReconciler) createInfisicalManagedKubeResource(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret, managedSecretReferenceInterface interface{}, secretsFromAPI []model.SingleEnvironmentVariable, ETag string, resourceType constants.ManagedKubeResourceType) error { + plainProcessedSecrets := make(map[string][]byte) + + var managedTemplateData *v1alpha1.SecretTemplate + + if resourceType == constants.MANAGED_KUBE_RESOURCE_TYPE_SECRET { + managedTemplateData = managedSecretReferenceInterface.(v1alpha1.ManagedKubeSecretConfig).Template + } else if resourceType == constants.MANAGED_KUBE_RESOURCE_TYPE_CONFIG_MAP { + managedTemplateData = managedSecretReferenceInterface.(v1alpha1.ManagedKubeConfigMapConfig).Template + } + + if managedTemplateData == nil || managedTemplateData.IncludeAllSecrets { + for _, secret := range secretsFromAPI { + plainProcessedSecrets[secret.Key] = []byte(secret.Value) // plain process + } + } + + if managedTemplateData != nil { + secretKeyValue := make(map[string]model.SecretTemplateOptions) + for _, secret := range secretsFromAPI { + secretKeyValue[secret.Key] = model.SecretTemplateOptions{ + Value: secret.Value, + SecretPath: secret.SecretPath, + } + } + + for templateKey, userTemplate := range managedTemplateData.Data { + tmpl, err := tpl.New("secret-templates").Funcs(template.GetTemplateFunctions()).Parse(userTemplate) + if err != nil { + return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err) + } + + buf := bytes.NewBuffer(nil) + err = tmpl.Execute(buf, secretKeyValue) + if err != nil { + return fmt.Errorf("unable to execute template: %s [err=%v]", templateKey, err) + } + plainProcessedSecrets[templateKey] = buf.Bytes() + } + } + + // copy labels and annotations from InfisicalSecret CRD + labels := map[string]string{} + for k, v := range infisicalSecret.Labels { + labels[k] = v + } + + annotations := map[string]string{} + systemPrefixes := []string{"kubectl.kubernetes.io/", "kubernetes.io/", "k8s.io/", "helm.sh/"} + for k, v := range infisicalSecret.Annotations { + isSystem := false + for _, prefix := range systemPrefixes { + if strings.HasPrefix(k, prefix) { + isSystem = true + break + } + } + if !isSystem { + annotations[k] = v + } + } + + if resourceType == constants.MANAGED_KUBE_RESOURCE_TYPE_SECRET { + + managedSecretReference := managedSecretReferenceInterface.(v1alpha1.ManagedKubeSecretConfig) + + annotations[constants.SECRET_VERSION_ANNOTATION] = ETag + // create a new secret as specified by the managed secret spec of CRD + newKubeSecretInstance := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: managedSecretReference.SecretName, + Namespace: managedSecretReference.SecretNamespace, + Annotations: annotations, + Labels: labels, + }, + Type: corev1.SecretType(managedSecretReference.SecretType), + Data: plainProcessedSecrets, + } + + if managedSecretReference.CreationPolicy == "Owner" { + // Set InfisicalSecret instance as the owner and controller of the managed secret + err := ctrl.SetControllerReference(&infisicalSecret, newKubeSecretInstance, r.Scheme) + if err != nil { + return err + } + } + + err := r.Client.Create(ctx, newKubeSecretInstance) + if err != nil { + return fmt.Errorf("unable to create the managed Kubernetes secret : %w", err) + } + logger.Info(fmt.Sprintf("Successfully created a managed Kubernetes secret with your Infisical secrets. Type: %s", managedSecretReference.SecretType)) + return nil + } else if resourceType == constants.MANAGED_KUBE_RESOURCE_TYPE_CONFIG_MAP { + + managedSecretReference := managedSecretReferenceInterface.(v1alpha1.ManagedKubeConfigMapConfig) + + // create a new config map as specified by the managed secret spec of CRD + newKubeConfigMapInstance := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: managedSecretReference.ConfigMapName, + Namespace: managedSecretReference.ConfigMapNamespace, + Annotations: annotations, + Labels: labels, + }, + Data: convertBinaryToStringMap(plainProcessedSecrets), + } + + if managedSecretReference.CreationPolicy == "Owner" { + // Set InfisicalSecret instance as the owner and controller of the managed config map + err := ctrl.SetControllerReference(&infisicalSecret, newKubeConfigMapInstance, r.Scheme) + if err != nil { + return err + } + } + + err := r.Client.Create(ctx, newKubeConfigMapInstance) + if err != nil { + return fmt.Errorf("unable to create the managed Kubernetes config map : %w", err) + } + logger.Info(fmt.Sprintf("Successfully created a managed Kubernetes config map with your Infisical secrets. Type: %s", managedSecretReference.ConfigMapName)) + return nil + + } + return fmt.Errorf("invalid resource type") + +} + +func (r *InfisicalSecretReconciler) updateInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, managedSecretReference v1alpha1.ManagedKubeSecretConfig, managedKubeSecret corev1.Secret, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error { + managedTemplateData := managedSecretReference.Template + + plainProcessedSecrets := make(map[string][]byte) + if managedTemplateData == nil || managedTemplateData.IncludeAllSecrets { + for _, secret := range secretsFromAPI { + plainProcessedSecrets[secret.Key] = []byte(secret.Value) + } + } + + if managedTemplateData != nil { + secretKeyValue := make(map[string]model.SecretTemplateOptions) + for _, secret := range secretsFromAPI { + secretKeyValue[secret.Key] = model.SecretTemplateOptions{ + Value: secret.Value, + SecretPath: secret.SecretPath, + } + } + + for templateKey, userTemplate := range managedTemplateData.Data { + tmpl, err := tpl.New("secret-templates").Funcs(template.GetTemplateFunctions()).Parse(userTemplate) + if err != nil { + return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err) + } + + buf := bytes.NewBuffer(nil) + err = tmpl.Execute(buf, secretKeyValue) + if err != nil { + return fmt.Errorf("unable to execute template: %s [err=%v]", templateKey, err) + } + plainProcessedSecrets[templateKey] = buf.Bytes() + } + } + + // Initialize the Annotations map if it's nil + if managedKubeSecret.ObjectMeta.Annotations == nil { + managedKubeSecret.ObjectMeta.Annotations = make(map[string]string) + } + + managedKubeSecret.Data = plainProcessedSecrets + managedKubeSecret.ObjectMeta.Annotations[constants.SECRET_VERSION_ANNOTATION] = ETag + + err := r.Client.Update(ctx, &managedKubeSecret) + if err != nil { + return fmt.Errorf("unable to update Kubernetes secret because [%w]", err) + } + + logger.Info("successfully updated managed Kubernetes secret") + return nil +} + +func (r *InfisicalSecretReconciler) updateInfisicalManagedConfigMap(ctx context.Context, logger logr.Logger, managedConfigMapReference v1alpha1.ManagedKubeConfigMapConfig, managedConfigMap corev1.ConfigMap, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error { + managedTemplateData := managedConfigMapReference.Template + + plainProcessedSecrets := make(map[string][]byte) + if managedTemplateData == nil || managedTemplateData.IncludeAllSecrets { + for _, secret := range secretsFromAPI { + plainProcessedSecrets[secret.Key] = []byte(secret.Value) + } + } + + if managedTemplateData != nil { + secretKeyValue := make(map[string]model.SecretTemplateOptions) + for _, secret := range secretsFromAPI { + secretKeyValue[secret.Key] = model.SecretTemplateOptions{ + Value: secret.Value, + SecretPath: secret.SecretPath, + } + } + + for templateKey, userTemplate := range managedTemplateData.Data { + tmpl, err := tpl.New("secret-templates").Funcs(template.GetTemplateFunctions()).Parse(userTemplate) + if err != nil { + return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err) + } + + buf := bytes.NewBuffer(nil) + err = tmpl.Execute(buf, secretKeyValue) + if err != nil { + return fmt.Errorf("unable to execute template: %s [err=%v]", templateKey, err) + } + plainProcessedSecrets[templateKey] = buf.Bytes() + } + } + + // Initialize the Annotations map if it's nil + if managedConfigMap.ObjectMeta.Annotations == nil { + managedConfigMap.ObjectMeta.Annotations = make(map[string]string) + } + + managedConfigMap.Data = convertBinaryToStringMap(plainProcessedSecrets) + managedConfigMap.ObjectMeta.Annotations[constants.SECRET_VERSION_ANNOTATION] = ETag + + err := r.Client.Update(ctx, &managedConfigMap) + if err != nil { + return fmt.Errorf("unable to update Kubernetes config map because [%w]", err) + } + + logger.Info("successfully updated managed Kubernetes config map") + return nil +} + +func (r *InfisicalSecretReconciler) fetchSecretsFromAPI(ctx context.Context, logger logr.Logger, authDetails util.AuthenticationDetails, infisicalClient infisicalSdk.InfisicalClientInterface, infisicalSecret v1alpha1.InfisicalSecret) ([]model.SingleEnvironmentVariable, error) { + + if authDetails.AuthStrategy == util.AuthStrategy.SERVICE_ACCOUNT { // Service Account // ! Legacy auth method + serviceAccountCreds, err := r.getInfisicalServiceAccountCredentialsFromKubeSecret(ctx, infisicalSecret) + if err != nil { + return nil, fmt.Errorf("ReconcileInfisicalSecret: unable to get service account creds from kube secret [err=%s]", err) + } + + plainTextSecretsFromApi, err := util.GetPlainTextSecretsViaServiceAccount(infisicalClient, serviceAccountCreds, infisicalSecret.Spec.Authentication.ServiceAccount.ProjectId, infisicalSecret.Spec.Authentication.ServiceAccount.EnvironmentName) + if err != nil { + return nil, fmt.Errorf("\nfailed to get secrets because [err=%v]", err) + } + + logger.Info("ReconcileInfisicalSecret: Fetched secrets via service account") + + return plainTextSecretsFromApi, nil + + } else if authDetails.AuthStrategy == util.AuthStrategy.SERVICE_TOKEN { // Service Tokens // ! Legacy / Deprecated auth method + infisicalToken, err := r.getInfisicalTokenFromKubeSecret(ctx, infisicalSecret) + if err != nil { + return nil, fmt.Errorf("ReconcileInfisicalSecret: unable to get service token from kube secret [err=%s]", err) + } + + envSlug := infisicalSecret.Spec.Authentication.ServiceToken.SecretsScope.EnvSlug + secretsPath := infisicalSecret.Spec.Authentication.ServiceToken.SecretsScope.SecretsPath + recursive := infisicalSecret.Spec.Authentication.ServiceToken.SecretsScope.Recursive + + plainTextSecretsFromApi, err := util.GetPlainTextSecretsViaServiceToken(infisicalClient, infisicalToken, envSlug, secretsPath, recursive) + if err != nil { + return nil, fmt.Errorf("\nfailed to get secrets because [err=%v]", err) + } + + logger.Info("ReconcileInfisicalSecret: Fetched secrets via [type=SERVICE_TOKEN]") + + return plainTextSecretsFromApi, nil + + } else if authDetails.IsMachineIdentityAuth { // * Machine Identity authentication, the SDK will be authenticated at this point + plainTextSecretsFromApi, err := util.GetPlainTextSecretsViaMachineIdentity(infisicalClient, authDetails.MachineIdentityScope) + + if err != nil { + return nil, fmt.Errorf("\nfailed to get secrets because [err=%v]", err) + } + + logger.Info(fmt.Sprintf("ReconcileInfisicalSecret: Fetched secrets via machine identity [type=%v]", authDetails.AuthStrategy)) + + return plainTextSecretsFromApi, nil + + } else { + return nil, errors.New("no authentication method provided. Please configure a authentication method then try again") + } +} + +func (r *InfisicalSecretReconciler) getResourceVariables(infisicalSecret v1alpha1.InfisicalSecret, resourceVariablesMap map[string]util.ResourceVariables) util.ResourceVariables { + + var resourceVariables util.ResourceVariables + + if _, ok := resourceVariablesMap[string(infisicalSecret.UID)]; !ok { + + ctx, cancel := context.WithCancel(context.Background()) + + client := infisicalSdk.NewInfisicalClient(ctx, infisicalSdk.Config{ + SiteUrl: api.API_HOST_URL, + CaCertificate: api.API_CA_CERTIFICATE, + UserAgent: api.USER_AGENT_NAME, + }) + + resourceVariablesMap[string(infisicalSecret.UID)] = util.ResourceVariables{ + InfisicalClient: client, + CancelCtx: cancel, + AuthDetails: util.AuthenticationDetails{}, + } + + resourceVariables = resourceVariablesMap[string(infisicalSecret.UID)] + + } else { + resourceVariables = resourceVariablesMap[string(infisicalSecret.UID)] + } + + return resourceVariables + +} + +func (r *InfisicalSecretReconciler) updateResourceVariables(infisicalSecret v1alpha1.InfisicalSecret, resourceVariables util.ResourceVariables, resourceVariablesMap map[string]util.ResourceVariables) { + resourceVariablesMap[string(infisicalSecret.UID)] = resourceVariables +} + +func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, managedKubeSecretReferences []v1alpha1.ManagedKubeSecretConfig, managedKubeConfigMapReferences []v1alpha1.ManagedKubeConfigMapConfig, resourceVariablesMap map[string]util.ResourceVariables) (int, error) { + + if infisicalSecret == nil { + return 0, fmt.Errorf("infisicalSecret is nil") + } + + resourceVariables := r.getResourceVariables(*infisicalSecret, resourceVariablesMap) + infisicalClient := resourceVariables.InfisicalClient + cancelCtx := resourceVariables.CancelCtx + authDetails := resourceVariables.AuthDetails + var err error + + if authDetails.AuthStrategy == "" { + logger.Info("No authentication strategy found. Attempting to authenticate") + authDetails, err = r.handleAuthentication(ctx, *infisicalSecret, infisicalClient) + r.SetInfisicalTokenLoadCondition(ctx, logger, infisicalSecret, authDetails.AuthStrategy, err) + + if err != nil { + return 0, fmt.Errorf("unable to authenticate [err=%s]", err) + } + + r.updateResourceVariables(*infisicalSecret, util.ResourceVariables{ + InfisicalClient: infisicalClient, + CancelCtx: cancelCtx, + AuthDetails: authDetails, + }, resourceVariablesMap) + } + + plainTextSecretsFromApi, err := r.fetchSecretsFromAPI(ctx, logger, authDetails, infisicalClient, *infisicalSecret) + + if err != nil { + return 0, fmt.Errorf("failed to fetch secrets from API for managed secrets [err=%s]", err) + } + secretsCount := len(plainTextSecretsFromApi) + + if len(managedKubeSecretReferences) > 0 { + for _, managedSecretReference := range managedKubeSecretReferences { + // Look for managed secret by name and namespace + managedKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{ + Name: managedSecretReference.SecretName, + Namespace: managedSecretReference.SecretNamespace, + }) + + if err != nil && !k8Errors.IsNotFound(err) { + return 0, fmt.Errorf("something went wrong when fetching the managed Kubernetes secret [%w]", err) + } + + newEtag := crypto.ComputeEtag([]byte(fmt.Sprintf("%v", plainTextSecretsFromApi))) + if managedKubeSecret == nil { + if err := r.createInfisicalManagedKubeResource(ctx, logger, *infisicalSecret, managedSecretReference, plainTextSecretsFromApi, newEtag, constants.MANAGED_KUBE_RESOURCE_TYPE_SECRET); err != nil { + return 0, fmt.Errorf("failed to create managed secret [err=%s]", err) + } + } else { + if err := r.updateInfisicalManagedKubeSecret(ctx, logger, managedSecretReference, *managedKubeSecret, plainTextSecretsFromApi, newEtag); err != nil { + return 0, fmt.Errorf("failed to update managed secret [err=%s]", err) + } + } + } + } + + if len(managedKubeConfigMapReferences) > 0 { + for _, managedConfigMapReference := range managedKubeConfigMapReferences { + managedKubeConfigMap, err := util.GetKubeConfigMapByNamespacedName(ctx, r.Client, types.NamespacedName{ + Name: managedConfigMapReference.ConfigMapName, + Namespace: managedConfigMapReference.ConfigMapNamespace, + }) + + if err != nil && !k8Errors.IsNotFound(err) { + return 0, fmt.Errorf("something went wrong when fetching the managed Kubernetes config map [%w]", err) + } + + newEtag := crypto.ComputeEtag([]byte(fmt.Sprintf("%v", plainTextSecretsFromApi))) + if managedKubeConfigMap == nil { + if err := r.createInfisicalManagedKubeResource(ctx, logger, *infisicalSecret, managedConfigMapReference, plainTextSecretsFromApi, newEtag, constants.MANAGED_KUBE_RESOURCE_TYPE_CONFIG_MAP); err != nil { + return 0, fmt.Errorf("failed to create managed config map [err=%s]", err) + } + } else { + if err := r.updateInfisicalManagedConfigMap(ctx, logger, managedConfigMapReference, *managedKubeConfigMap, plainTextSecretsFromApi, newEtag); err != nil { + return 0, fmt.Errorf("failed to update managed config map [err=%s]", err) + } + } + + } + } + + return secretsCount, nil +} diff --git a/k8-operator/k8-operator/internal/services/infisicalsecret/suite_test.go b/k8-operator/k8-operator/internal/services/infisicalsecret/suite_test.go new file mode 100644 index 000000000..6b93568a1 --- /dev/null +++ b/k8-operator/k8-operator/internal/services/infisicalsecret/suite_test.go @@ -0,0 +1,64 @@ +package infisicalsecret + +import ( + "path/filepath" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/rest" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + //+kubebuilder:scaffold:imports +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var cfg *rest.Config +var k8sClient client.Client +var testEnv *envtest.Environment + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Controller Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, + ErrorIfCRDPathMissing: true, + } + + var err error + // cfg is defined in this file globally. + cfg, err = testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + err = secretsv1alpha1.AddToScheme(scheme.Scheme) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + +}) + +var _ = AfterSuite(func() { + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) diff --git a/k8-operator/k8-operator/internal/template/base64.go b/k8-operator/k8-operator/internal/template/base64.go new file mode 100644 index 000000000..3fff06c86 --- /dev/null +++ b/k8-operator/k8-operator/internal/template/base64.go @@ -0,0 +1,18 @@ +package template + +import ( + "encoding/base64" + "fmt" +) + +func decodeBase64ToBytes(encodedString string) string { + decoded, err := base64.StdEncoding.DecodeString(encodedString) + if err != nil { + panic(fmt.Sprintf("Error: %v", err)) + } + return string(decoded) +} + +func encodeBase64(plainString string) string { + return base64.StdEncoding.EncodeToString([]byte(plainString)) +} diff --git a/k8-operator/k8-operator/internal/template/jwk.go b/k8-operator/k8-operator/internal/template/jwk.go new file mode 100644 index 000000000..8dbc8f379 --- /dev/null +++ b/k8-operator/k8-operator/internal/template/jwk.go @@ -0,0 +1,43 @@ +package template + +import ( + "crypto/x509" + "fmt" + + "github.com/lestrrat-go/jwx/v2/jwk" +) + +func jwkPublicKeyPem(jwkjson string) string { + k, err := jwk.ParseKey([]byte(jwkjson)) + if err != nil { + panic(fmt.Sprintf("[jwkPublicKeyPem] Error: %v", err)) + } + var rawkey any + err = k.Raw(&rawkey) + if err != nil { + panic(fmt.Sprintf("[jwkPublicKeyPem] Error: %v", err)) + } + mpk, err := x509.MarshalPKIXPublicKey(rawkey) + if err != nil { + panic(fmt.Sprintf("[jwkPublicKeyPem] Error: %v", err)) + } + return pemEncode(mpk, "PUBLIC KEY") +} + +func jwkPrivateKeyPem(jwkjson string) string { + k, err := jwk.ParseKey([]byte(jwkjson)) + if err != nil { + panic(fmt.Sprintf("[jwkPrivateKeyPem] Error: %v", err)) + } + var mpk []byte + var pk any + err = k.Raw(&pk) + if err != nil { + panic(fmt.Sprintf("[jwkPrivateKeyPem] Error: %v", err)) + } + mpk, err = x509.MarshalPKCS8PrivateKey(pk) + if err != nil { + panic(fmt.Sprintf("[jwkPrivateKeyPem] Error: %v", err)) + } + return pemEncode(mpk, "PRIVATE KEY") +} diff --git a/k8-operator/k8-operator/internal/template/pem.go b/k8-operator/k8-operator/internal/template/pem.go new file mode 100644 index 000000000..f37a9d576 --- /dev/null +++ b/k8-operator/k8-operator/internal/template/pem.go @@ -0,0 +1,98 @@ +package template + +import ( + "bytes" + "crypto/x509" + "encoding/pem" + "fmt" + "strings" +) + +const ( + errJunk = "error filtering pem: found junk" + + certTypeLeaf = "leaf" + certTypeIntermediate = "intermediate" + certTypeRoot = "root" +) + +func filterPEM(pemType, input string) string { + data := []byte(input) + var blocks []byte + var block *pem.Block + var rest []byte + for { + block, rest = pem.Decode(data) + data = rest + + if block == nil { + break + } + if !strings.EqualFold(block.Type, pemType) { + continue + } + + var buf bytes.Buffer + err := pem.Encode(&buf, block) + if err != nil { + panic(fmt.Sprintf("[filterPEM] Error: %v", err)) + } + blocks = append(blocks, buf.Bytes()...) + } + + if len(blocks) == 0 && len(rest) != 0 { + panic(fmt.Sprintf("[filterPEM] Error: %v", errJunk)) + } + + return string(blocks) +} + +func filterCertChain(certType, input string) string { + ordered := fetchX509CertChains([]byte(input)) + + switch certType { + case certTypeLeaf: + cert := ordered[0] + if cert.AuthorityKeyId != nil && !bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId) { + return pemEncode(ordered[0].Raw, pemTypeCertificate) + } + case certTypeIntermediate: + if len(ordered) < 2 { + return "" + } + var pemData []byte + for _, cert := range ordered[1:] { + if isRootCertificate(cert) { + break + } + b := &pem.Block{ + Type: pemTypeCertificate, + Bytes: cert.Raw, + } + pemData = append(pemData, pem.EncodeToMemory(b)...) + } + return string(pemData) + case certTypeRoot: + cert := ordered[len(ordered)-1] + if isRootCertificate(cert) { + return pemEncode(cert.Raw, pemTypeCertificate) + } + } + + return "" +} + +func isRootCertificate(cert *x509.Certificate) bool { + return cert.AuthorityKeyId == nil || bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId) +} + +func pemEncode(thing []byte, kind string) string { + buf := bytes.NewBuffer(nil) + err := pem.Encode(buf, &pem.Block{Type: kind, Bytes: thing}) + + if err != nil { + panic(fmt.Sprintf("[pemEncode] Error: %v", err)) + } + + return buf.String() +} diff --git a/k8-operator/k8-operator/internal/template/pem_chain.go b/k8-operator/k8-operator/internal/template/pem_chain.go new file mode 100644 index 000000000..00c4f5d3f --- /dev/null +++ b/k8-operator/k8-operator/internal/template/pem_chain.go @@ -0,0 +1,117 @@ +package template + +import ( + "bytes" + "crypto/x509" + "encoding/pem" + "fmt" +) + +const ( + errNilCert = "certificate is nil" + errFoundDisjunctCert = "found multiple leaf or disjunct certificates" + errNoLeafFound = "no leaf certificate found" + errChainCycle = "constructing chain resulted in cycle" +) + +type node struct { + cert *x509.Certificate + parent *node + isParent bool +} + +func fetchX509CertChains(data []byte) []*x509.Certificate { + var newCertChain []*x509.Certificate + nodes := pemToNodes(data) + + // at the end of this computation, the output will be a single linked list + // the tail of the list will be the root node (which has no parents) + // the head of the list will be the leaf node (whose parent will be intermediate certs) + // (head) leaf -> intermediates -> root (tail) + for i := range nodes { + for j := range nodes { + // ignore same node to prevent generating a cycle + if i == j { + continue + } + // if ith node AuthorityKeyId is same as jth node SubjectKeyId, jth node was used + // to sign the ith certificate + if bytes.Equal(nodes[i].cert.AuthorityKeyId, nodes[j].cert.SubjectKeyId) { + nodes[j].isParent = true + nodes[i].parent = nodes[j] + break + } + } + } + + var foundLeaf bool + var leaf *node + for i := range nodes { + if !nodes[i].isParent { + if foundLeaf { + panic(fmt.Sprintf("[fetchX509CertChains] Error: %v", errFoundDisjunctCert)) + } + // this is the leaf node as it's not a parent for any other node + leaf = nodes[i] + foundLeaf = true + } + } + + if leaf == nil { + panic(fmt.Sprintf("[fetchX509CertChains] Error: %v", errNoLeafFound)) + } + + processedNodes := 0 + // iterate through the directed list and append the nodes to new cert chain + for leaf != nil { + processedNodes++ + // ensure we aren't stuck in a cyclic loop + if processedNodes > len(nodes) { + panic(fmt.Sprintf("[fetchX509CertChains] Error: %v", errChainCycle)) + } + newCertChain = append(newCertChain, leaf.cert) + leaf = leaf.parent + } + return newCertChain +} + +func fetchCertChains(data []byte) []byte { + var pemData []byte + newCertChain := fetchX509CertChains(data) + + for _, cert := range newCertChain { + b := &pem.Block{ + Type: pemTypeCertificate, + Bytes: cert.Raw, + } + pemData = append(pemData, pem.EncodeToMemory(b)...) + } + return pemData +} + +func pemToNodes(data []byte) []*node { + nodes := make([]*node, 0) + for { + // decode pem to der first + block, rest := pem.Decode(data) + data = rest + + if block == nil { + break + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + panic(fmt.Sprintf("[pemToNodes] Error: %v", err)) + } + + if cert == nil { + panic(fmt.Sprintf("[pemToNodes] Error: %v", errNilCert)) + } + nodes = append(nodes, &node{ + cert: cert, + parent: nil, + isParent: false, + }) + } + return nodes +} diff --git a/k8-operator/k8-operator/internal/template/pkcs12.go b/k8-operator/k8-operator/internal/template/pkcs12.go new file mode 100644 index 000000000..e6763fc46 --- /dev/null +++ b/k8-operator/k8-operator/internal/template/pkcs12.go @@ -0,0 +1,144 @@ +package template + +import ( + "bytes" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "fmt" + + gopkcs12 "software.sslmate.com/src/go-pkcs12" +) + +func pkcs12keyPass(pass, input string) string { + privateKey, _, _, err := gopkcs12.DecodeChain([]byte(input), pass) + if err != nil { + panic(fmt.Sprintf("Error: %v", err)) + } + + marshalPrivateKey, err := x509.MarshalPKCS8PrivateKey(privateKey) + if err != nil { + panic(fmt.Sprintf("Error: %v", err)) + } + + var buf bytes.Buffer + if err := pem.Encode(&buf, &pem.Block{ + Type: pemTypeKey, + Bytes: marshalPrivateKey, + }); err != nil { + panic(fmt.Sprintf("Error: %v", err)) + } + return buf.String() +} + +func parsePrivateKey(block []byte) any { + if k, err := x509.ParsePKCS1PrivateKey(block); err == nil { + return k + } + if k, err := x509.ParsePKCS8PrivateKey(block); err == nil { + return k + } + if k, err := x509.ParseECPrivateKey(block); err == nil { + return k + } + panic("Error: unable to parse private key") +} + +func pkcs12key(input string) string { + return pkcs12keyPass("", input) +} + +func pkcs12certPass(pass, input string) string { + _, certificate, caCerts, err := gopkcs12.DecodeChain([]byte(input), pass) + if err != nil { + panic(fmt.Sprintf("Error: %v", err)) + } + + var pemData []byte + var buf bytes.Buffer + if err := pem.Encode(&buf, &pem.Block{ + Type: pemTypeCertificate, + Bytes: certificate.Raw, + }); err != nil { + panic(fmt.Sprintf("Error: %v", err)) + } + + pemData = append(pemData, buf.Bytes()...) + + for _, ca := range caCerts { + var buf bytes.Buffer + if err := pem.Encode(&buf, &pem.Block{ + Type: pemTypeCertificate, + Bytes: ca.Raw, + }); err != nil { + panic(fmt.Sprintf("Error: %v", err)) + } + pemData = append(pemData, buf.Bytes()...) + } + + // try to order certificate chain. If it fails we return + // the unordered raw pem data. + // This fails if multiple leaf or disjunct certs are provided. + ordered := fetchCertChains(pemData) + + return string(ordered) +} + +func pkcs12cert(input string) string { + return pkcs12certPass("", input) +} + +func pemToPkcs12(cert, key string) string { + return pemToPkcs12Pass(cert, key, "") +} + +func pemToPkcs12Pass(cert, key, pass string) string { + certPem, _ := pem.Decode([]byte(cert)) + + parsedCert, err := x509.ParseCertificate(certPem.Bytes) + if err != nil { + panic(fmt.Sprintf("Error: %v", err)) + } + + return certsToPkcs12(parsedCert, key, nil, pass) +} + +func fullPemToPkcs12(cert, key string) string { + return fullPemToPkcs12Pass(cert, key, "") +} + +func fullPemToPkcs12Pass(cert, key, pass string) string { + certPem, rest := pem.Decode([]byte(cert)) + + parsedCert, err := x509.ParseCertificate(certPem.Bytes) + if err != nil { + panic(fmt.Sprintf("Error: %v", err)) + } + + caCerts := make([]*x509.Certificate, 0) + for len(rest) > 0 { + caPem, restBytes := pem.Decode(rest) + rest = restBytes + + caCert, err := x509.ParseCertificate(caPem.Bytes) + if err != nil { + panic(fmt.Sprintf("Error: %v", err)) + } + + caCerts = append(caCerts, caCert) + } + + return certsToPkcs12(parsedCert, key, caCerts, pass) +} + +func certsToPkcs12(cert *x509.Certificate, key string, caCerts []*x509.Certificate, password string) string { + keyPem, _ := pem.Decode([]byte(key)) + parsedKey := parsePrivateKey(keyPem.Bytes) + + pfx, err := gopkcs12.Modern.Encode(parsedKey, cert, caCerts, password) + if err != nil { + panic(fmt.Sprintf("Error: %v", err)) + } + + return base64.StdEncoding.EncodeToString(pfx) +} diff --git a/k8-operator/k8-operator/internal/template/template.go b/k8-operator/k8-operator/internal/template/template.go new file mode 100644 index 000000000..d56b3da5d --- /dev/null +++ b/k8-operator/k8-operator/internal/template/template.go @@ -0,0 +1,67 @@ +package template + +import ( + tpl "text/template" + + "github.com/Masterminds/sprig/v3" +) + +var customInfisicalSecretTemplateFunctions = tpl.FuncMap{ + "pkcs12key": pkcs12key, + "pkcs12keyPass": pkcs12keyPass, + "pkcs12cert": pkcs12cert, + "pkcs12certPass": pkcs12certPass, + + "pemToPkcs12": pemToPkcs12, + "pemToPkcs12Pass": pemToPkcs12Pass, + "fullPemToPkcs12": fullPemToPkcs12, + "fullPemToPkcs12Pass": fullPemToPkcs12Pass, + + "filterPEM": filterPEM, + "filterCertChain": filterCertChain, + + "jwkPublicKeyPem": jwkPublicKeyPem, + "jwkPrivateKeyPem": jwkPrivateKeyPem, + + "toYaml": toYAML, + "fromYaml": fromYAML, + + "decodeBase64ToBytes": decodeBase64ToBytes, + "encodeBase64": encodeBase64, +} + +const ( + errParse = "unable to parse template at key %s: %s" + errExecute = "unable to execute template at key %s: %s" + errDecodePKCS12WithPass = "unable to decode pkcs12 with password: %s" + errDecodeCertWithPass = "unable to decode pkcs12 certificate with password: %s" + errParsePrivKey = "unable to parse private key type" + errUnmarshalJSON = "unable to unmarshal json: %s" + errMarshalJSON = "unable to marshal json: %s" + + pemTypeCertificate = "CERTIFICATE" + pemTypeKey = "PRIVATE KEY" +) + +func InitializeTemplateFunctions() { + templates := customInfisicalSecretTemplateFunctions + + sprigFuncs := sprig.TxtFuncMap() + // removed for security reasons + delete(sprigFuncs, "env") + delete(sprigFuncs, "expandenv") + + for k, v := range sprigFuncs { + // make sure we aren't overwriting any of our own functions + _, exists := templates[k] + if !exists { + templates[k] = v + } + } + + customInfisicalSecretTemplateFunctions = templates +} + +func GetTemplateFunctions() tpl.FuncMap { + return customInfisicalSecretTemplateFunctions +} diff --git a/k8-operator/k8-operator/internal/template/yaml.go b/k8-operator/k8-operator/internal/template/yaml.go new file mode 100644 index 000000000..5352d5a02 --- /dev/null +++ b/k8-operator/k8-operator/internal/template/yaml.go @@ -0,0 +1,30 @@ +package template + +import ( + "fmt" + "strings" + + "gopkg.in/yaml.v3" +) + +func toYAML(v any) string { + data, err := yaml.Marshal(v) + if err != nil { + panic(fmt.Sprintf("Error: %v", err)) + + } + return strings.TrimSuffix(string(data), "\n") +} + +// fromYAML converts a YAML document into a map[string]any. +// +// This is not a general-purpose YAML parser, and will not parse all valid +// YAML documents. +func fromYAML(str string) map[string]any { + mapData := map[string]any{} + + if err := yaml.Unmarshal([]byte(str), &mapData); err != nil { + panic(fmt.Sprintf("Error: %v", err)) + } + return mapData +} diff --git a/k8-operator/k8-operator/internal/util/auth.go b/k8-operator/k8-operator/internal/util/auth.go new file mode 100644 index 000000000..7305ca45e --- /dev/null +++ b/k8-operator/k8-operator/internal/util/auth.go @@ -0,0 +1,490 @@ +package util + +import ( + "context" + "fmt" + + "errors" + + corev1 "k8s.io/api/core/v1" + + authenticationv1 "k8s.io/api/authentication/v1" + + "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + "github.com/aws/smithy-go/ptr" + infisicalSdk "github.com/infisical/go-sdk" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func GetServiceAccountToken(k8sClient client.Client, namespace string, serviceAccountName string, autoCreateServiceAccountToken bool, serviceAccountTokenAudiences []string) (string, error) { + + if autoCreateServiceAccountToken { + restClient, err := GetRestClientFromClient() + if err != nil { + return "", fmt.Errorf("failed to get REST client: %w", err) + } + + tokenRequest := &authenticationv1.TokenRequest{ + Spec: authenticationv1.TokenRequestSpec{ + ExpirationSeconds: ptr.Int64(600), // 10 minutes. the token only needs to be valid for when we do the initial k8s login. + }, + } + + if len(serviceAccountTokenAudiences) > 0 { + // Conditionally add the audiences if they are specified. + // Failing to do this causes a default audience to be used, which is not what we want if the user doesn't specify any. + tokenRequest.Spec.Audiences = serviceAccountTokenAudiences + } + + result := &authenticationv1.TokenRequest{} + err = restClient. + Post(). + Namespace(namespace). + Resource("serviceaccounts"). + Name(serviceAccountName). + SubResource("token"). + Body(tokenRequest). + Do(context.Background()). + Into(result) + + if err != nil { + return "", fmt.Errorf("failed to create token: %w", err) + } + + return result.Status.Token, nil + } + + serviceAccount := &corev1.ServiceAccount{} + err := k8sClient.Get(context.TODO(), client.ObjectKey{Name: serviceAccountName, Namespace: namespace}, serviceAccount) + if err != nil { + return "", err + } + + if len(serviceAccount.Secrets) == 0 { + return "", fmt.Errorf("no secrets found for service account %s", serviceAccountName) + } + + secretName := serviceAccount.Secrets[0].Name + + secret := &corev1.Secret{} + err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: secretName, Namespace: namespace}, secret) + if err != nil { + return "", err + } + + token := secret.Data["token"] + + return string(token), nil +} + +type AuthStrategyType string + +var AuthStrategy = struct { + SERVICE_TOKEN AuthStrategyType + SERVICE_ACCOUNT AuthStrategyType + UNIVERSAL_MACHINE_IDENTITY AuthStrategyType + KUBERNETES_MACHINE_IDENTITY AuthStrategyType + AWS_IAM_MACHINE_IDENTITY AuthStrategyType + AZURE_MACHINE_IDENTITY AuthStrategyType + GCP_ID_TOKEN_MACHINE_IDENTITY AuthStrategyType + GCP_IAM_MACHINE_IDENTITY AuthStrategyType +}{ + SERVICE_TOKEN: "SERVICE_TOKEN", + SERVICE_ACCOUNT: "SERVICE_ACCOUNT", + UNIVERSAL_MACHINE_IDENTITY: "UNIVERSAL_MACHINE_IDENTITY", + KUBERNETES_MACHINE_IDENTITY: "KUBERNETES_AUTH_MACHINE_IDENTITY", + AWS_IAM_MACHINE_IDENTITY: "AWS_IAM_MACHINE_IDENTITY", + AZURE_MACHINE_IDENTITY: "AZURE_MACHINE_IDENTITY", + GCP_ID_TOKEN_MACHINE_IDENTITY: "GCP_ID_TOKEN_MACHINE_IDENTITY", + GCP_IAM_MACHINE_IDENTITY: "GCP_IAM_MACHINE_IDENTITY", +} + +type SecretCrdType string + +var SecretCrd = struct { + INFISICAL_SECRET SecretCrdType + INFISICAL_PUSH_SECRET SecretCrdType + INFISICAL_DYNAMIC_SECRET SecretCrdType +}{ + INFISICAL_SECRET: "INFISICAL_SECRET", + INFISICAL_PUSH_SECRET: "INFISICAL_PUSH_SECRET", + INFISICAL_DYNAMIC_SECRET: "INFISICAL_DYNAMIC_SECRET", +} + +type SecretAuthInput struct { + Secret interface{} + Type SecretCrdType +} + +type AuthenticationDetails struct { + AuthStrategy AuthStrategyType + MachineIdentityScope v1alpha1.MachineIdentityScopeInWorkspace // This will only be set if a machine identity auth method is used (e.g. UniversalAuth or KubernetesAuth, etc.) + IsMachineIdentityAuth bool + SecretType SecretCrdType +} + +var ErrAuthNotApplicable = errors.New("authentication not applicable") + +func HandleUniversalAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { + + var universalAuthSpec v1alpha1.UniversalAuthDetails + + switch secretCrd.Type { + case SecretCrd.INFISICAL_SECRET: + infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret") + } + universalAuthSpec = infisicalSecret.Spec.Authentication.UniversalAuth + case SecretCrd.INFISICAL_PUSH_SECRET: + infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret") + } + + universalAuthSpec = v1alpha1.UniversalAuthDetails{ + CredentialsRef: infisicalPushSecret.Spec.Authentication.UniversalAuth.CredentialsRef, + SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, + } + + case SecretCrd.INFISICAL_DYNAMIC_SECRET: + infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret") + } + + universalAuthSpec = v1alpha1.UniversalAuthDetails{ + CredentialsRef: infisicalDynamicSecret.Spec.Authentication.UniversalAuth.CredentialsRef, + SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, + } + } + + universalAuthKubeSecret, err := GetInfisicalUniversalAuthFromKubeSecret(ctx, reconcilerClient, v1alpha1.KubeSecretReference{ + SecretNamespace: universalAuthSpec.CredentialsRef.SecretNamespace, + SecretName: universalAuthSpec.CredentialsRef.SecretName, + }) + + if err != nil { + return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get machine identity creds from kube secret [err=%s]", err) + } + + if universalAuthKubeSecret.ClientId == "" && universalAuthKubeSecret.ClientSecret == "" { + return AuthenticationDetails{}, ErrAuthNotApplicable + } + + _, err = infisicalClient.Auth().UniversalAuthLogin(universalAuthKubeSecret.ClientId, universalAuthKubeSecret.ClientSecret) + if err != nil { + return AuthenticationDetails{}, fmt.Errorf("unable to login with machine identity credentials [err=%s]", err) + } + + return AuthenticationDetails{ + AuthStrategy: AuthStrategy.UNIVERSAL_MACHINE_IDENTITY, + MachineIdentityScope: universalAuthSpec.SecretsScope, + IsMachineIdentityAuth: true, + SecretType: secretCrd.Type, + }, nil +} + +func HandleKubernetesAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { + var kubernetesAuthSpec v1alpha1.KubernetesAuthDetails + + switch secretCrd.Type { + case SecretCrd.INFISICAL_SECRET: + infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret") + } + kubernetesAuthSpec = infisicalSecret.Spec.Authentication.KubernetesAuth + case SecretCrd.INFISICAL_PUSH_SECRET: + infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret") + } + kubernetesAuthSpec = v1alpha1.KubernetesAuthDetails{ + IdentityID: infisicalPushSecret.Spec.Authentication.KubernetesAuth.IdentityID, + ServiceAccountRef: v1alpha1.KubernetesServiceAccountRef{ + Namespace: infisicalPushSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Namespace, + Name: infisicalPushSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Name, + }, + SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, + AutoCreateServiceAccountToken: infisicalPushSecret.Spec.Authentication.KubernetesAuth.AutoCreateServiceAccountToken, + ServiceAccountTokenAudiences: infisicalPushSecret.Spec.Authentication.KubernetesAuth.ServiceAccountTokenAudiences, + } + + case SecretCrd.INFISICAL_DYNAMIC_SECRET: + infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret") + } + + kubernetesAuthSpec = v1alpha1.KubernetesAuthDetails{ + IdentityID: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.IdentityID, + ServiceAccountRef: v1alpha1.KubernetesServiceAccountRef{ + Namespace: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Namespace, + Name: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Name, + }, + SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, + AutoCreateServiceAccountToken: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.AutoCreateServiceAccountToken, + ServiceAccountTokenAudiences: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.ServiceAccountTokenAudiences, + } + } + + if kubernetesAuthSpec.IdentityID == "" { + return AuthenticationDetails{}, ErrAuthNotApplicable + } + + serviceAccountToken, err := GetServiceAccountToken( + reconcilerClient, + kubernetesAuthSpec.ServiceAccountRef.Namespace, + kubernetesAuthSpec.ServiceAccountRef.Name, + kubernetesAuthSpec.AutoCreateServiceAccountToken, + kubernetesAuthSpec.ServiceAccountTokenAudiences, + ) + + if err != nil { + return AuthenticationDetails{}, fmt.Errorf("unable to get service account token [err=%s]", err) + } + + _, err = infisicalClient.Auth().KubernetesRawServiceAccountTokenLogin(kubernetesAuthSpec.IdentityID, serviceAccountToken) + if err != nil { + return AuthenticationDetails{}, fmt.Errorf("unable to login with Kubernetes native auth [err=%s]", err) + } + + return AuthenticationDetails{ + AuthStrategy: AuthStrategy.KUBERNETES_MACHINE_IDENTITY, + MachineIdentityScope: kubernetesAuthSpec.SecretsScope, + IsMachineIdentityAuth: true, + SecretType: secretCrd.Type, + }, nil + +} + +func HandleAwsIamAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { + awsIamAuthSpec := v1alpha1.AWSIamAuthDetails{} + + switch secretCrd.Type { + case SecretCrd.INFISICAL_SECRET: + infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret") + } + + awsIamAuthSpec = infisicalSecret.Spec.Authentication.AwsIamAuth + case SecretCrd.INFISICAL_PUSH_SECRET: + infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret") + } + + awsIamAuthSpec = v1alpha1.AWSIamAuthDetails{ + IdentityID: infisicalPushSecret.Spec.Authentication.AwsIamAuth.IdentityID, + SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, + } + + case SecretCrd.INFISICAL_DYNAMIC_SECRET: + infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret") + } + + awsIamAuthSpec = v1alpha1.AWSIamAuthDetails{ + IdentityID: infisicalDynamicSecret.Spec.Authentication.AwsIamAuth.IdentityID, + SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, + } + } + + if awsIamAuthSpec.IdentityID == "" { + return AuthenticationDetails{}, ErrAuthNotApplicable + } + + _, err := infisicalClient.Auth().AwsIamAuthLogin(awsIamAuthSpec.IdentityID) + if err != nil { + return AuthenticationDetails{}, fmt.Errorf("unable to login with AWS IAM auth [err=%s]", err) + } + + return AuthenticationDetails{ + AuthStrategy: AuthStrategy.AWS_IAM_MACHINE_IDENTITY, + MachineIdentityScope: awsIamAuthSpec.SecretsScope, + IsMachineIdentityAuth: true, + SecretType: secretCrd.Type, + }, nil + +} + +func HandleAzureAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { + azureAuthSpec := v1alpha1.AzureAuthDetails{} + + switch secretCrd.Type { + case SecretCrd.INFISICAL_SECRET: + infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret") + } + + azureAuthSpec = infisicalSecret.Spec.Authentication.AzureAuth + + case SecretCrd.INFISICAL_PUSH_SECRET: + infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret") + } + + azureAuthSpec = v1alpha1.AzureAuthDetails{ + IdentityID: infisicalPushSecret.Spec.Authentication.AzureAuth.IdentityID, + Resource: infisicalPushSecret.Spec.Authentication.AzureAuth.Resource, + SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, + } + + case SecretCrd.INFISICAL_DYNAMIC_SECRET: + infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret") + } + + azureAuthSpec = v1alpha1.AzureAuthDetails{ + IdentityID: infisicalDynamicSecret.Spec.Authentication.AzureAuth.IdentityID, + Resource: infisicalDynamicSecret.Spec.Authentication.AzureAuth.Resource, + SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, + } + } + + if azureAuthSpec.IdentityID == "" { + return AuthenticationDetails{}, ErrAuthNotApplicable + } + + _, err := infisicalClient.Auth().AzureAuthLogin(azureAuthSpec.IdentityID, azureAuthSpec.Resource) // If resource is empty(""), it will default to "https://management.azure.com/" in the SDK. + if err != nil { + return AuthenticationDetails{}, fmt.Errorf("unable to login with Azure auth [err=%s]", err) + } + + return AuthenticationDetails{ + AuthStrategy: AuthStrategy.AZURE_MACHINE_IDENTITY, + MachineIdentityScope: azureAuthSpec.SecretsScope, + IsMachineIdentityAuth: true, + SecretType: secretCrd.Type, + }, nil + +} + +func HandleGcpIdTokenAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { + gcpIdTokenSpec := v1alpha1.GCPIdTokenAuthDetails{} + + switch secretCrd.Type { + case SecretCrd.INFISICAL_SECRET: + infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret") + } + + gcpIdTokenSpec = infisicalSecret.Spec.Authentication.GcpIdTokenAuth + case SecretCrd.INFISICAL_PUSH_SECRET: + infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret") + } + + gcpIdTokenSpec = v1alpha1.GCPIdTokenAuthDetails{ + IdentityID: infisicalPushSecret.Spec.Authentication.GcpIdTokenAuth.IdentityID, + SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, + } + + case SecretCrd.INFISICAL_DYNAMIC_SECRET: + infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret") + } + + gcpIdTokenSpec = v1alpha1.GCPIdTokenAuthDetails{ + IdentityID: infisicalDynamicSecret.Spec.Authentication.GcpIdTokenAuth.IdentityID, + SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, + } + } + + if gcpIdTokenSpec.IdentityID == "" { + return AuthenticationDetails{}, ErrAuthNotApplicable + } + + _, err := infisicalClient.Auth().GcpIdTokenAuthLogin(gcpIdTokenSpec.IdentityID) + if err != nil { + return AuthenticationDetails{}, fmt.Errorf("unable to login with GCP Id Token auth [err=%s]", err) + } + + return AuthenticationDetails{ + AuthStrategy: AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY, + MachineIdentityScope: gcpIdTokenSpec.SecretsScope, + IsMachineIdentityAuth: true, + SecretType: secretCrd.Type, + }, nil + +} + +func HandleGcpIamAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { + gcpIamSpec := v1alpha1.GcpIamAuthDetails{} + + switch secretCrd.Type { + case SecretCrd.INFISICAL_SECRET: + infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret") + } + + gcpIamSpec = infisicalSecret.Spec.Authentication.GcpIamAuth + case SecretCrd.INFISICAL_PUSH_SECRET: + infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret") + } + + gcpIamSpec = v1alpha1.GcpIamAuthDetails{ + IdentityID: infisicalPushSecret.Spec.Authentication.GcpIamAuth.IdentityID, + ServiceAccountKeyFilePath: infisicalPushSecret.Spec.Authentication.GcpIamAuth.ServiceAccountKeyFilePath, + SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, + } + + case SecretCrd.INFISICAL_DYNAMIC_SECRET: + infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret") + } + + gcpIamSpec = v1alpha1.GcpIamAuthDetails{ + IdentityID: infisicalDynamicSecret.Spec.Authentication.GcpIamAuth.IdentityID, + ServiceAccountKeyFilePath: infisicalDynamicSecret.Spec.Authentication.GcpIamAuth.ServiceAccountKeyFilePath, + SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, + } + } + + if gcpIamSpec.IdentityID == "" && gcpIamSpec.ServiceAccountKeyFilePath == "" { + return AuthenticationDetails{}, ErrAuthNotApplicable + } + + _, err := infisicalClient.Auth().GcpIamAuthLogin(gcpIamSpec.IdentityID, gcpIamSpec.ServiceAccountKeyFilePath) + if err != nil { + return AuthenticationDetails{}, fmt.Errorf("unable to login with GCP IAM auth [err=%s]", err) + } + + return AuthenticationDetails{ + AuthStrategy: AuthStrategy.GCP_IAM_MACHINE_IDENTITY, + MachineIdentityScope: gcpIamSpec.SecretsScope, + IsMachineIdentityAuth: true, + SecretType: secretCrd.Type, + }, nil +} diff --git a/k8-operator/k8-operator/internal/util/helpers.go b/k8-operator/k8-operator/internal/util/helpers.go new file mode 100644 index 000000000..ef3712715 --- /dev/null +++ b/k8-operator/k8-operator/internal/util/helpers.go @@ -0,0 +1,56 @@ +package util + +import ( + "fmt" + "strconv" + "strings" + "time" +) + +func ConvertIntervalToDuration(resyncInterval *string) (time.Duration, error) { + + if resyncInterval == nil || *resyncInterval == "" { + return 0, nil + } + + length := len(*resyncInterval) + if length < 2 { + return 0, fmt.Errorf("invalid format") + } + + unit := (*resyncInterval)[length-1:] + numberPart := (*resyncInterval)[:length-1] + + number, err := strconv.Atoi(numberPart) + if err != nil { + return 0, err + } + + switch unit { + case "s": + if number < 5 { + return 0, fmt.Errorf("resync interval must be at least 5 seconds") + } + return time.Duration(number) * time.Second, nil + case "m": + return time.Duration(number) * time.Minute, nil + case "h": + return time.Duration(number) * time.Hour, nil + case "d": + return time.Duration(number) * 24 * time.Hour, nil + case "w": + return time.Duration(number) * 7 * 24 * time.Hour, nil + default: + return 0, fmt.Errorf("invalid time unit") + } +} + +func AppendAPIEndpoint(address string) string { + if strings.HasSuffix(address, "/api") { + return address + } + if address[len(address)-1] == '/' { + return address + "api" + } + return address + "/api" +} diff --git a/k8-operator/k8-operator/internal/util/kubernetes.go b/k8-operator/k8-operator/internal/util/kubernetes.go new file mode 100644 index 000000000..a50af803b --- /dev/null +++ b/k8-operator/k8-operator/internal/util/kubernetes.go @@ -0,0 +1,92 @@ +package util + +import ( + "context" + "fmt" + + "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + "github.com/Infisical/infisical/k8-operator/internal/model" + corev1 "k8s.io/api/core/v1" + k8Errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +const INFISICAL_MACHINE_IDENTITY_CLIENT_ID = "clientId" +const INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET = "clientSecret" + +func GetKubeSecretByNamespacedName(ctx context.Context, reconcilerClient client.Client, namespacedName types.NamespacedName) (*corev1.Secret, error) { + kubeSecret := &corev1.Secret{} + err := reconcilerClient.Get(ctx, namespacedName, kubeSecret) + if err != nil { + kubeSecret = nil + } + + return kubeSecret, err +} + +func GetKubeConfigMapByNamespacedName(ctx context.Context, reconcilerClient client.Client, namespacedName types.NamespacedName) (*corev1.ConfigMap, error) { + kubeConfigMap := &corev1.ConfigMap{} + err := reconcilerClient.Get(ctx, namespacedName, kubeConfigMap) + if err != nil { + kubeConfigMap = nil + } + + return kubeConfigMap, err +} + +func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, universalAuthRef v1alpha1.KubeSecretReference) (machineIdentityDetails model.MachineIdentityDetails, err error) { + + universalAuthCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{ + Namespace: universalAuthRef.SecretNamespace, + Name: universalAuthRef.SecretName, + // Namespace: infisicalSecret.Spec.Authentication.UniversalAuth.CredentialsRef.SecretNamespace, + // Name: infisicalSecret.Spec.Authentication.UniversalAuth.CredentialsRef.SecretName, + }) + + if k8Errors.IsNotFound(err) { + return model.MachineIdentityDetails{}, nil + } + + if err != nil { + return model.MachineIdentityDetails{}, fmt.Errorf("something went wrong when fetching your machine identity credentials [err=%s]", err) + } + + clientIdFromSecret := universalAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_CLIENT_ID] + clientSecretFromSecret := universalAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET] + + return model.MachineIdentityDetails{ClientId: string(clientIdFromSecret), ClientSecret: string(clientSecretFromSecret)}, nil + +} + +func getKubeClusterConfig() (*rest.Config, error) { + config, err := rest.InClusterConfig() + if err != nil { + + loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() + configOverrides := &clientcmd.ConfigOverrides{} + kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) + return kubeConfig.ClientConfig() + } + + return config, nil +} + +func GetRestClientFromClient() (rest.Interface, error) { + + config, err := getKubeClusterConfig() + if err != nil { + return nil, err + } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + + return clientset.CoreV1().RESTClient(), nil + +} diff --git a/k8-operator/k8-operator/internal/util/models.go b/k8-operator/k8-operator/internal/util/models.go new file mode 100644 index 000000000..8030731c2 --- /dev/null +++ b/k8-operator/k8-operator/internal/util/models.go @@ -0,0 +1,13 @@ +package util + +import ( + "context" + + infisicalSdk "github.com/infisical/go-sdk" +) + +type ResourceVariables struct { + InfisicalClient infisicalSdk.InfisicalClientInterface + CancelCtx context.CancelFunc + AuthDetails AuthenticationDetails +} diff --git a/k8-operator/k8-operator/internal/util/secrets.go b/k8-operator/k8-operator/internal/util/secrets.go new file mode 100644 index 000000000..88aa342e9 --- /dev/null +++ b/k8-operator/k8-operator/internal/util/secrets.go @@ -0,0 +1,186 @@ +package util + +import ( + "fmt" + "strings" + + "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + "github.com/Infisical/infisical/k8-operator/internal/api" + "github.com/Infisical/infisical/k8-operator/internal/model" + "github.com/go-resty/resty/v2" + infisical "github.com/infisical/go-sdk" +) + +type DecodedSymmetricEncryptionDetails = struct { + Cipher []byte + IV []byte + Tag []byte + Key []byte +} + +func VerifyServiceToken(serviceToken string) (string, error) { + serviceTokenParts := strings.SplitN(serviceToken, ".", 4) + if len(serviceTokenParts) < 4 { + return "", fmt.Errorf("invalid service token entered. Please double check your service token and try again") + } + + serviceToken = fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2]) + return serviceToken, nil +} + +func GetServiceTokenDetails(infisicalToken string) (api.GetServiceTokenDetailsResponse, error) { + serviceTokenParts := strings.SplitN(infisicalToken, ".", 4) + if len(serviceTokenParts) < 4 { + return api.GetServiceTokenDetailsResponse{}, fmt.Errorf("invalid service token entered. Please double check your service token and try again") + } + + serviceToken := fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2]) + + httpClient := resty.New() + httpClient.SetAuthToken(serviceToken). + SetHeader("Accept", "application/json") + + serviceTokenDetails, err := api.CallGetServiceTokenDetailsV2(httpClient) + if err != nil { + return api.GetServiceTokenDetailsResponse{}, fmt.Errorf("unable to get service token details. [err=%v]", err) + } + + return serviceTokenDetails, nil +} + +func GetPlainTextSecretsViaMachineIdentity(infisicalClient infisical.InfisicalClientInterface, secretScope v1alpha1.MachineIdentityScopeInWorkspace) ([]model.SingleEnvironmentVariable, error) { + + secrets, err := infisicalClient.Secrets().List(infisical.ListSecretsOptions{ + ProjectSlug: secretScope.ProjectSlug, + Environment: secretScope.EnvSlug, + Recursive: secretScope.Recursive, + SecretPath: secretScope.SecretsPath, + IncludeImports: true, + ExpandSecretReferences: true, + }) + + if err != nil { + return nil, fmt.Errorf("unable to get secrets. [err=%v]", err) + } + + var environmentVariables []model.SingleEnvironmentVariable + + for _, secret := range secrets { + + environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{ + Key: secret.SecretKey, + Value: secret.SecretValue, + Type: secret.Type, + ID: secret.ID, + SecretPath: secret.SecretPath, + }) + } + + return environmentVariables, nil +} + +func GetPlainTextSecretsViaServiceToken(infisicalClient infisical.InfisicalClientInterface, fullServiceToken string, envSlug string, secretPath string, recursive bool) ([]model.SingleEnvironmentVariable, error) { + serviceTokenParts := strings.SplitN(fullServiceToken, ".", 4) + if len(serviceTokenParts) < 4 { + return nil, fmt.Errorf("invalid service token entered. Please double check your service token and try again") + } + + serviceToken := fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2]) + + httpClient := resty.New() + + httpClient.SetAuthToken(serviceToken). + SetHeader("Accept", "application/json") + + serviceTokenDetails, err := api.CallGetServiceTokenDetailsV2(httpClient) + if err != nil { + return nil, fmt.Errorf("unable to get service token details. [err=%v]", err) + } + + secrets, err := infisicalClient.Secrets().List(infisical.ListSecretsOptions{ + ProjectID: serviceTokenDetails.Workspace, + Environment: envSlug, + Recursive: recursive, + SecretPath: secretPath, + IncludeImports: true, + ExpandSecretReferences: true, + }) + + if err != nil { + return nil, err + } + + var environmentVariables []model.SingleEnvironmentVariable + + for _, secret := range secrets { + + environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{ + Key: secret.SecretKey, + Value: secret.SecretValue, + Type: secret.Type, + ID: secret.ID, + SecretPath: secret.SecretPath, + }) + } + + return environmentVariables, nil + +} + +// Fetches plaintext secrets from an API endpoint using a service account. +// The function fetches the service account details and keys, decrypts the workspace key, fetches the encrypted secrets for the specified project and environment, and decrypts the secrets using the decrypted workspace key. +// Returns the plaintext secrets, encrypted secrets response, and any errors that occurred during the process. +func GetPlainTextSecretsViaServiceAccount(infisicalClient infisical.InfisicalClientInterface, serviceAccountCreds model.ServiceAccountDetails, projectId string, environmentName string) ([]model.SingleEnvironmentVariable, error) { + httpClient := resty.New() + httpClient.SetAuthToken(serviceAccountCreds.AccessKey). + SetHeader("Accept", "application/json") + + serviceAccountDetails, err := api.CallGetServiceTokenAccountDetailsV2(httpClient) + if err != nil { + return nil, fmt.Errorf("GetPlainTextSecretsViaServiceAccount: unable to get service account details. [err=%v]", err) + } + + serviceAccountKeys, err := api.CallGetServiceAccountKeysV2(httpClient, api.GetServiceAccountKeysRequest{ServiceAccountId: serviceAccountDetails.ServiceAccount.ID}) + if err != nil { + return nil, fmt.Errorf("GetPlainTextSecretsViaServiceAccount: unable to get service account key details. [err=%v]", err) + } + + // find key for requested project + var workspaceServiceAccountKey api.ServiceAccountKey + for _, serviceAccountKey := range serviceAccountKeys.ServiceAccountKeys { + if serviceAccountKey.Workspace == projectId { + workspaceServiceAccountKey = serviceAccountKey + } + } + + if workspaceServiceAccountKey.ID == "" || workspaceServiceAccountKey.EncryptedKey == "" || workspaceServiceAccountKey.Nonce == "" || serviceAccountCreds.PublicKey == "" || serviceAccountCreds.PrivateKey == "" { + return nil, fmt.Errorf("unable to find key for [projectId=%s] [err=%v]. Ensure that the given service account has access to given projectId", projectId, err) + } + + secrets, err := infisicalClient.Secrets().List(infisical.ListSecretsOptions{ + ProjectID: projectId, + Environment: environmentName, + Recursive: false, + SecretPath: "/", + IncludeImports: true, + ExpandSecretReferences: true, + }) + + if err != nil { + return nil, err + } + + var environmentVariables []model.SingleEnvironmentVariable + + for _, secret := range secrets { + environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{ + Key: secret.SecretKey, + Value: secret.SecretValue, + Type: secret.Type, + ID: secret.ID, + SecretPath: secret.SecretPath, + }) + } + + return environmentVariables, nil +} diff --git a/k8-operator/k8-operator/internal/util/time.go b/k8-operator/k8-operator/internal/util/time.go new file mode 100644 index 000000000..0b78a16a6 --- /dev/null +++ b/k8-operator/k8-operator/internal/util/time.go @@ -0,0 +1,40 @@ +package util + +import ( + "fmt" + "strconv" + "time" +) + +func ConvertResyncIntervalToDuration(resyncInterval string) (time.Duration, error) { + length := len(resyncInterval) + if length < 2 { + return 0, fmt.Errorf("invalid format") + } + + unit := resyncInterval[length-1:] + numberPart := resyncInterval[:length-1] + + number, err := strconv.Atoi(numberPart) + if err != nil { + return 0, err + } + + switch unit { + case "s": + if number < 5 { + return 0, fmt.Errorf("resync interval must be at least 5 seconds") + } + return time.Duration(number) * time.Second, nil + case "m": + return time.Duration(number) * time.Minute, nil + case "h": + return time.Duration(number) * time.Hour, nil + case "d": + return time.Duration(number) * 24 * time.Hour, nil + case "w": + return time.Duration(number) * 7 * 24 * time.Hour, nil + default: + return 0, fmt.Errorf("invalid time unit") + } +} diff --git a/k8-operator/k8-operator/internal/util/workspace.go b/k8-operator/k8-operator/internal/util/workspace.go new file mode 100644 index 000000000..d62c0288a --- /dev/null +++ b/k8-operator/k8-operator/internal/util/workspace.go @@ -0,0 +1,27 @@ +package util + +import ( + "fmt" + + "github.com/Infisical/infisical/k8-operator/internal/api" + "github.com/Infisical/infisical/k8-operator/internal/model" + "github.com/go-resty/resty/v2" +) + +func GetProjectByID(accessToken string, projectId string) (model.Project, error) { + + httpClient := resty.New() + httpClient. + SetAuthScheme("Bearer"). + SetAuthToken(accessToken). + SetHeader("Accept", "application/json") + + projectDetails, err := api.CallGetProjectByID(httpClient, api.GetProjectByIDRequest{ + ProjectID: projectId, + }) + if err != nil { + return model.Project{}, fmt.Errorf("unable to get project by slug. [err=%v]", err) + } + + return projectDetails.Project, nil +} diff --git a/k8-operator/k8-operator/test/e2e/e2e_suite_test.go b/k8-operator/k8-operator/test/e2e/e2e_suite_test.go new file mode 100644 index 000000000..f4f91405f --- /dev/null +++ b/k8-operator/k8-operator/test/e2e/e2e_suite_test.go @@ -0,0 +1,89 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "fmt" + "os" + "os/exec" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/Infisical/infisical/k8-operator/test/utils" +) + +var ( + // Optional Environment Variables: + // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup. + // These variables are useful if CertManager is already installed, avoiding + // re-installation and conflicts. + skipCertManagerInstall = os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true" + // isCertManagerAlreadyInstalled will be set true when CertManager CRDs be found on the cluster + isCertManagerAlreadyInstalled = false + + // projectImage is the name of the image which will be build and loaded + // with the code source changes to be tested. + projectImage = "example.com/k8-operator:v0.0.1" +) + +// TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated, +// temporary environment to validate project changes with the purpose of being used in CI jobs. +// The default setup requires Kind, builds/loads the Manager Docker image locally, and installs +// CertManager. +func TestE2E(t *testing.T) { + RegisterFailHandler(Fail) + _, _ = fmt.Fprintf(GinkgoWriter, "Starting k8-operator integration test suite\n") + RunSpecs(t, "e2e suite") +} + +var _ = BeforeSuite(func() { + By("building the manager(Operator) image") + cmd := exec.Command("make", "docker-build", fmt.Sprintf("IMG=%s", projectImage)) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image") + + // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is + // built and available before running the tests. Also, remove the following block. + By("loading the manager(Operator) image on Kind") + err = utils.LoadImageToKindClusterWithName(projectImage) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind") + + // The tests-e2e are intended to run on a temporary cluster that is created and destroyed for testing. + // To prevent errors when tests run in environments with CertManager already installed, + // we check for its presence before execution. + // Setup CertManager before the suite if not skipped and if not already installed + if !skipCertManagerInstall { + By("checking if cert manager is installed already") + isCertManagerAlreadyInstalled = utils.IsCertManagerCRDsInstalled() + if !isCertManagerAlreadyInstalled { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n") + Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager") + } else { + _, _ = fmt.Fprintf(GinkgoWriter, "WARNING: CertManager is already installed. Skipping installation...\n") + } + } +}) + +var _ = AfterSuite(func() { + // Teardown CertManager after the suite if not skipped and if it was not already installed + if !skipCertManagerInstall && !isCertManagerAlreadyInstalled { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n") + utils.UninstallCertManager() + } +}) diff --git a/k8-operator/k8-operator/test/e2e/e2e_test.go b/k8-operator/k8-operator/test/e2e/e2e_test.go new file mode 100644 index 000000000..15b26328c --- /dev/null +++ b/k8-operator/k8-operator/test/e2e/e2e_test.go @@ -0,0 +1,330 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/Infisical/infisical/k8-operator/test/utils" +) + +// namespace where the project is deployed in +const namespace = "k8-operator-system" + +// serviceAccountName created for the project +const serviceAccountName = "k8-operator-controller-manager" + +// metricsServiceName is the name of the metrics service of the project +const metricsServiceName = "k8-operator-controller-manager-metrics-service" + +// metricsRoleBindingName is the name of the RBAC that will be created to allow get the metrics data +const metricsRoleBindingName = "k8-operator-metrics-binding" + +var _ = Describe("Manager", Ordered, func() { + var controllerPodName string + + // Before running the tests, set up the environment by creating the namespace, + // enforce the restricted security policy to the namespace, installing CRDs, + // and deploying the controller. + BeforeAll(func() { + By("creating manager namespace") + cmd := exec.Command("kubectl", "create", "ns", namespace) + _, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create namespace") + + By("labeling the namespace to enforce the restricted security policy") + cmd = exec.Command("kubectl", "label", "--overwrite", "ns", namespace, + "pod-security.kubernetes.io/enforce=restricted") + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to label namespace with restricted policy") + + By("installing CRDs") + cmd = exec.Command("make", "install") + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to install CRDs") + + By("deploying the controller-manager") + cmd = exec.Command("make", "deploy", fmt.Sprintf("IMG=%s", projectImage)) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager") + }) + + // After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs, + // and deleting the namespace. + AfterAll(func() { + By("cleaning up the curl pod for metrics") + cmd := exec.Command("kubectl", "delete", "pod", "curl-metrics", "-n", namespace) + _, _ = utils.Run(cmd) + + By("undeploying the controller-manager") + cmd = exec.Command("make", "undeploy") + _, _ = utils.Run(cmd) + + By("uninstalling CRDs") + cmd = exec.Command("make", "uninstall") + _, _ = utils.Run(cmd) + + By("removing manager namespace") + cmd = exec.Command("kubectl", "delete", "ns", namespace) + _, _ = utils.Run(cmd) + }) + + // After each test, check for failures and collect logs, events, + // and pod descriptions for debugging. + AfterEach(func() { + specReport := CurrentSpecReport() + if specReport.Failed() { + By("Fetching controller manager pod logs") + cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace) + controllerLogs, err := utils.Run(cmd) + if err == nil { + _, _ = fmt.Fprintf(GinkgoWriter, "Controller logs:\n %s", controllerLogs) + } else { + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Controller logs: %s", err) + } + + By("Fetching Kubernetes events") + cmd = exec.Command("kubectl", "get", "events", "-n", namespace, "--sort-by=.lastTimestamp") + eventsOutput, err := utils.Run(cmd) + if err == nil { + _, _ = fmt.Fprintf(GinkgoWriter, "Kubernetes events:\n%s", eventsOutput) + } else { + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Kubernetes events: %s", err) + } + + By("Fetching curl-metrics logs") + cmd = exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace) + metricsOutput, err := utils.Run(cmd) + if err == nil { + _, _ = fmt.Fprintf(GinkgoWriter, "Metrics logs:\n %s", metricsOutput) + } else { + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get curl-metrics logs: %s", err) + } + + By("Fetching controller manager pod description") + cmd = exec.Command("kubectl", "describe", "pod", controllerPodName, "-n", namespace) + podDescription, err := utils.Run(cmd) + if err == nil { + fmt.Println("Pod description:\n", podDescription) + } else { + fmt.Println("Failed to describe controller pod") + } + } + }) + + SetDefaultEventuallyTimeout(2 * time.Minute) + SetDefaultEventuallyPollingInterval(time.Second) + + Context("Manager", func() { + It("should run successfully", func() { + By("validating that the controller-manager pod is running as expected") + verifyControllerUp := func(g Gomega) { + // Get the name of the controller-manager pod + cmd := exec.Command("kubectl", "get", + "pods", "-l", "control-plane=controller-manager", + "-o", "go-template={{ range .items }}"+ + "{{ if not .metadata.deletionTimestamp }}"+ + "{{ .metadata.name }}"+ + "{{ \"\\n\" }}{{ end }}{{ end }}", + "-n", namespace, + ) + + podOutput, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information") + podNames := utils.GetNonEmptyLines(podOutput) + g.Expect(podNames).To(HaveLen(1), "expected 1 controller pod running") + controllerPodName = podNames[0] + g.Expect(controllerPodName).To(ContainSubstring("controller-manager")) + + // Validate the pod's status + cmd = exec.Command("kubectl", "get", + "pods", controllerPodName, "-o", "jsonpath={.status.phase}", + "-n", namespace, + ) + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(Equal("Running"), "Incorrect controller-manager pod status") + } + Eventually(verifyControllerUp).Should(Succeed()) + }) + + It("should ensure the metrics endpoint is serving metrics", func() { + By("creating a ClusterRoleBinding for the service account to allow access to metrics") + cmd := exec.Command("kubectl", "create", "clusterrolebinding", metricsRoleBindingName, + "--clusterrole=k8-operator-metrics-reader", + fmt.Sprintf("--serviceaccount=%s:%s", namespace, serviceAccountName), + ) + _, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create ClusterRoleBinding") + + By("validating that the metrics service is available") + cmd = exec.Command("kubectl", "get", "service", metricsServiceName, "-n", namespace) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Metrics service should exist") + + By("getting the service account token") + token, err := serviceAccountToken() + Expect(err).NotTo(HaveOccurred()) + Expect(token).NotTo(BeEmpty()) + + By("waiting for the metrics endpoint to be ready") + verifyMetricsEndpointReady := func(g Gomega) { + cmd := exec.Command("kubectl", "get", "endpoints", metricsServiceName, "-n", namespace) + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(ContainSubstring("8443"), "Metrics endpoint is not ready") + } + Eventually(verifyMetricsEndpointReady).Should(Succeed()) + + By("verifying that the controller manager is serving the metrics server") + verifyMetricsServerStarted := func(g Gomega) { + cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace) + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(ContainSubstring("controller-runtime.metrics\tServing metrics server"), + "Metrics server not yet started") + } + Eventually(verifyMetricsServerStarted).Should(Succeed()) + + By("creating the curl-metrics pod to access the metrics endpoint") + cmd = exec.Command("kubectl", "run", "curl-metrics", "--restart=Never", + "--namespace", namespace, + "--image=curlimages/curl:latest", + "--overrides", + fmt.Sprintf(`{ + "spec": { + "containers": [{ + "name": "curl", + "image": "curlimages/curl:latest", + "command": ["/bin/sh", "-c"], + "args": ["curl -v -k -H 'Authorization: Bearer %s' https://%s.%s.svc.cluster.local:8443/metrics"], + "securityContext": { + "readOnlyRootFilesystem": true, + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "runAsUser": 1000, + "seccompProfile": { + "type": "RuntimeDefault" + } + } + }], + "serviceAccountName": "%s" + } + }`, token, metricsServiceName, namespace, serviceAccountName)) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create curl-metrics pod") + + By("waiting for the curl-metrics pod to complete.") + verifyCurlUp := func(g Gomega) { + cmd := exec.Command("kubectl", "get", "pods", "curl-metrics", + "-o", "jsonpath={.status.phase}", + "-n", namespace) + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(Equal("Succeeded"), "curl pod in wrong status") + } + Eventually(verifyCurlUp, 5*time.Minute).Should(Succeed()) + + By("getting the metrics by checking curl-metrics logs") + metricsOutput := getMetricsOutput() + Expect(metricsOutput).To(ContainSubstring( + "controller_runtime_reconcile_total", + )) + }) + + // +kubebuilder:scaffold:e2e-webhooks-checks + + // TODO: Customize the e2e test suite with scenarios specific to your project. + // Consider applying sample/CR(s) and check their status and/or verifying + // the reconciliation by using the metrics, i.e.: + // metricsOutput := getMetricsOutput() + // Expect(metricsOutput).To(ContainSubstring( + // fmt.Sprintf(`controller_runtime_reconcile_total{controller="%s",result="success"} 1`, + // strings.ToLower(), + // )) + }) +}) + +// serviceAccountToken returns a token for the specified service account in the given namespace. +// It uses the Kubernetes TokenRequest API to generate a token by directly sending a request +// and parsing the resulting token from the API response. +func serviceAccountToken() (string, error) { + const tokenRequestRawString = `{ + "apiVersion": "authentication.k8s.io/v1", + "kind": "TokenRequest" + }` + + // Temporary file to store the token request + secretName := fmt.Sprintf("%s-token-request", serviceAccountName) + tokenRequestFile := filepath.Join("/tmp", secretName) + err := os.WriteFile(tokenRequestFile, []byte(tokenRequestRawString), os.FileMode(0o644)) + if err != nil { + return "", err + } + + var out string + verifyTokenCreation := func(g Gomega) { + // Execute kubectl command to create the token + cmd := exec.Command("kubectl", "create", "--raw", fmt.Sprintf( + "/api/v1/namespaces/%s/serviceaccounts/%s/token", + namespace, + serviceAccountName, + ), "-f", tokenRequestFile) + + output, err := cmd.CombinedOutput() + g.Expect(err).NotTo(HaveOccurred()) + + // Parse the JSON output to extract the token + var token tokenRequest + err = json.Unmarshal(output, &token) + g.Expect(err).NotTo(HaveOccurred()) + + out = token.Status.Token + } + Eventually(verifyTokenCreation).Should(Succeed()) + + return out, err +} + +// getMetricsOutput retrieves and returns the logs from the curl pod used to access the metrics endpoint. +func getMetricsOutput() string { + By("getting the curl-metrics logs") + cmd := exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace) + metricsOutput, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to retrieve logs from curl pod") + Expect(metricsOutput).To(ContainSubstring("< HTTP/1.1 200 OK")) + return metricsOutput +} + +// tokenRequest is a simplified representation of the Kubernetes TokenRequest API response, +// containing only the token field that we need to extract. +type tokenRequest struct { + Status struct { + Token string `json:"token"` + } `json:"status"` +} diff --git a/k8-operator/k8-operator/test/utils/utils.go b/k8-operator/k8-operator/test/utils/utils.go new file mode 100644 index 000000000..841683609 --- /dev/null +++ b/k8-operator/k8-operator/test/utils/utils.go @@ -0,0 +1,254 @@ +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package utils + +import ( + "bufio" + "bytes" + "fmt" + "os" + "os/exec" + "strings" + + . "github.com/onsi/ginkgo/v2" // nolint:revive,staticcheck +) + +const ( + prometheusOperatorVersion = "v0.77.1" + prometheusOperatorURL = "https://github.com/prometheus-operator/prometheus-operator/" + + "releases/download/%s/bundle.yaml" + + certmanagerVersion = "v1.16.3" + certmanagerURLTmpl = "https://github.com/cert-manager/cert-manager/releases/download/%s/cert-manager.yaml" +) + +func warnError(err error) { + _, _ = fmt.Fprintf(GinkgoWriter, "warning: %v\n", err) +} + +// Run executes the provided command within this context +func Run(cmd *exec.Cmd) (string, error) { + dir, _ := GetProjectDir() + cmd.Dir = dir + + if err := os.Chdir(cmd.Dir); err != nil { + _, _ = fmt.Fprintf(GinkgoWriter, "chdir dir: %q\n", err) + } + + cmd.Env = append(os.Environ(), "GO111MODULE=on") + command := strings.Join(cmd.Args, " ") + _, _ = fmt.Fprintf(GinkgoWriter, "running: %q\n", command) + output, err := cmd.CombinedOutput() + if err != nil { + return string(output), fmt.Errorf("%q failed with error %q: %w", command, string(output), err) + } + + return string(output), nil +} + +// InstallPrometheusOperator installs the prometheus Operator to be used to export the enabled metrics. +func InstallPrometheusOperator() error { + url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) + cmd := exec.Command("kubectl", "create", "-f", url) + _, err := Run(cmd) + return err +} + +// UninstallPrometheusOperator uninstalls the prometheus +func UninstallPrometheusOperator() { + url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) + cmd := exec.Command("kubectl", "delete", "-f", url) + if _, err := Run(cmd); err != nil { + warnError(err) + } +} + +// IsPrometheusCRDsInstalled checks if any Prometheus CRDs are installed +// by verifying the existence of key CRDs related to Prometheus. +func IsPrometheusCRDsInstalled() bool { + // List of common Prometheus CRDs + prometheusCRDs := []string{ + "prometheuses.monitoring.coreos.com", + "prometheusrules.monitoring.coreos.com", + "prometheusagents.monitoring.coreos.com", + } + + cmd := exec.Command("kubectl", "get", "crds", "-o", "custom-columns=NAME:.metadata.name") + output, err := Run(cmd) + if err != nil { + return false + } + crdList := GetNonEmptyLines(output) + for _, crd := range prometheusCRDs { + for _, line := range crdList { + if strings.Contains(line, crd) { + return true + } + } + } + + return false +} + +// UninstallCertManager uninstalls the cert manager +func UninstallCertManager() { + url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) + cmd := exec.Command("kubectl", "delete", "-f", url) + if _, err := Run(cmd); err != nil { + warnError(err) + } +} + +// InstallCertManager installs the cert manager bundle. +func InstallCertManager() error { + url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) + cmd := exec.Command("kubectl", "apply", "-f", url) + if _, err := Run(cmd); err != nil { + return err + } + // Wait for cert-manager-webhook to be ready, which can take time if cert-manager + // was re-installed after uninstalling on a cluster. + cmd = exec.Command("kubectl", "wait", "deployment.apps/cert-manager-webhook", + "--for", "condition=Available", + "--namespace", "cert-manager", + "--timeout", "5m", + ) + + _, err := Run(cmd) + return err +} + +// IsCertManagerCRDsInstalled checks if any Cert Manager CRDs are installed +// by verifying the existence of key CRDs related to Cert Manager. +func IsCertManagerCRDsInstalled() bool { + // List of common Cert Manager CRDs + certManagerCRDs := []string{ + "certificates.cert-manager.io", + "issuers.cert-manager.io", + "clusterissuers.cert-manager.io", + "certificaterequests.cert-manager.io", + "orders.acme.cert-manager.io", + "challenges.acme.cert-manager.io", + } + + // Execute the kubectl command to get all CRDs + cmd := exec.Command("kubectl", "get", "crds") + output, err := Run(cmd) + if err != nil { + return false + } + + // Check if any of the Cert Manager CRDs are present + crdList := GetNonEmptyLines(output) + for _, crd := range certManagerCRDs { + for _, line := range crdList { + if strings.Contains(line, crd) { + return true + } + } + } + + return false +} + +// LoadImageToKindClusterWithName loads a local docker image to the kind cluster +func LoadImageToKindClusterWithName(name string) error { + cluster := "kind" + if v, ok := os.LookupEnv("KIND_CLUSTER"); ok { + cluster = v + } + kindOptions := []string{"load", "docker-image", name, "--name", cluster} + cmd := exec.Command("kind", kindOptions...) + _, err := Run(cmd) + return err +} + +// GetNonEmptyLines converts given command output string into individual objects +// according to line breakers, and ignores the empty elements in it. +func GetNonEmptyLines(output string) []string { + var res []string + elements := strings.Split(output, "\n") + for _, element := range elements { + if element != "" { + res = append(res, element) + } + } + + return res +} + +// GetProjectDir will return the directory where the project is +func GetProjectDir() (string, error) { + wd, err := os.Getwd() + if err != nil { + return wd, fmt.Errorf("failed to get current working directory: %w", err) + } + wd = strings.ReplaceAll(wd, "/test/e2e", "") + return wd, nil +} + +// UncommentCode searches for target in the file and remove the comment prefix +// of the target content. The target content may span multiple lines. +func UncommentCode(filename, target, prefix string) error { + // false positive + // nolint:gosec + content, err := os.ReadFile(filename) + if err != nil { + return fmt.Errorf("failed to read file %q: %w", filename, err) + } + strContent := string(content) + + idx := strings.Index(strContent, target) + if idx < 0 { + return fmt.Errorf("unable to find the code %q to be uncomment", target) + } + + out := new(bytes.Buffer) + _, err = out.Write(content[:idx]) + if err != nil { + return fmt.Errorf("failed to write to output: %w", err) + } + + scanner := bufio.NewScanner(bytes.NewBufferString(target)) + if !scanner.Scan() { + return nil + } + for { + if _, err = out.WriteString(strings.TrimPrefix(scanner.Text(), prefix)); err != nil { + return fmt.Errorf("failed to write to output: %w", err) + } + // Avoid writing a newline in case the previous line was the last in target. + if !scanner.Scan() { + break + } + if _, err = out.WriteString("\n"); err != nil { + return fmt.Errorf("failed to write to output: %w", err) + } + } + + if _, err = out.Write(content[idx+len(target):]); err != nil { + return fmt.Errorf("failed to write to output: %w", err) + } + + // false positive + // nolint:gosec + if err = os.WriteFile(filename, out.Bytes(), 0644); err != nil { + return fmt.Errorf("failed to write file %q: %w", filename, err) + } + + return nil +} From c12408eb816064fd6b62485a3a83dec8e77cf1ef Mon Sep 17 00:00:00 2001 From: = Date: Fri, 25 Jul 2025 01:06:47 +0530 Subject: [PATCH 2/9] feat: migrated the operator code to v4 --- k8-operator/Dockerfile | 9 +- k8-operator/Makefile | 193 +- k8-operator/PROJECT | 57 +- .../api/v1alpha1/zz_generated.deepcopy.go | 3 +- k8-operator/{k8-operator => }/cmd/main.go | 19 +- ...crets.infisical.com_clustergenerators.yaml | 42 +- ...infisical.com_infisicaldynamicsecrets.yaml | 91 +- ...ts.infisical.com_infisicalpushsecrets.yaml | 80 +- ...ecrets.infisical.com_infisicalsecrets.yaml | 124 +- .../secrets.infisical.com_passwords.yaml | 44 +- .../bases/secrets.infisical.com_uuids.yaml | 20 +- .../cainjection_in_infisicalsecrets.yaml | 7 - .../patches/webhook_in_infisicalsecrets.yaml | 16 - .../default/cert_metrics_manager_patch.yaml | 0 k8-operator/config/default/kustomization.yaml | 252 +- .../default/manager_auth_proxy_patch.yaml | 55 - .../config/default/manager_config_patch.yaml | 10 - .../config/default/manager_metrics_patch.yaml | 0 .../config/default/metrics_service.yaml | 0 k8-operator/config/manager/kustomization.yaml | 6 - k8-operator/config/manager/manager.yaml | 35 +- .../network-policy/allow-metrics-traffic.yaml | 0 .../config/network-policy/kustomization.yaml | 0 .../config/prometheus/kustomization.yaml | 9 + k8-operator/config/prometheus/monitor.yaml | 15 +- .../config/prometheus/monitor_tls_patch.yaml | 0 .../rbac/auth_proxy_client_clusterrole.yaml | 16 - k8-operator/config/rbac/auth_proxy_role.yaml | 24 - .../config/rbac/auth_proxy_role_binding.yaml | 19 - .../config/rbac/auth_proxy_service.yaml | 21 - .../infisicaldynamicsecret_admin_role.yaml | 0 .../infisicaldynamicsecret_editor_role.yaml | 8 +- .../infisicaldynamicsecret_viewer_role.yaml | 8 +- .../rbac/infisicalpushsecret_editor_role.yaml | 27 - .../rbac/infisicalpushsecret_viewer_role.yaml | 23 - .../infisicalpushsecretsecret_admin_role.yaml | 0 ...infisicalpushsecretsecret_editor_role.yaml | 0 ...infisicalpushsecretsecret_viewer_role.yaml | 0 .../rbac/infisicalsecret_admin_role.yaml | 0 .../rbac/infisicalsecret_editor_role.yaml | 14 +- .../rbac/infisicalsecret_viewer_role.yaml | 14 +- k8-operator/config/rbac/kustomization.yaml | 30 +- .../config/rbac/leader_election_role.yaml | 6 +- .../rbac/leader_election_role_binding.yaml | 6 +- .../config/rbac/metrics_auth_role.yaml | 0 .../rbac/metrics_auth_role_binding.yaml | 0 .../config/rbac/metrics_reader_role.yaml | 0 k8-operator/config/rbac/role.yaml | 89 +- k8-operator/config/rbac/role_binding.yaml | 6 +- k8-operator/config/rbac/service_account.yaml | 6 +- .../infisicaldynamicsecret/dynamicSecret.yaml | 10 +- .../infisicalsecret/infisicalSecretCrd.yaml | 23 +- .../samples/crd/pushsecret/push-secret.yaml | 26 +- .../samples/universalAuthIdentitySecret.yaml | 6 +- .../infisicaldynamicsecret_controller.go | 217 -- .../controllers/infisicalsecret/conditions.go | 100 - .../infisicalsecret_controller.go | 212 -- .../controllers/infisicalsecret/suite_test.go | 64 - k8-operator/go.mod | 197 +- k8-operator/go.sum | 734 ++---- k8-operator/hack/boilerplate.go.txt | 2 +- .../{k8-operator => }/internal/api/api.go | 0 .../{k8-operator => }/internal/api/models.go | 0 .../internal/api/variables.go | 0 .../internal/constants/constants.go | 0 .../infisicaldynamicsecret_controller.go | 209 +- .../infisicaldynamicsecret_controller_test.go | 0 .../infisicalpushsecret_controller.go | 104 +- .../infisicalpushsecret_controller_test.go} | 8 +- .../controller/infisicalsecret_controller.go | 12 +- .../infisicalsecret_controller_test.go | 0 .../internal/controller/suite_test.go | 0 .../controllerhelpers/controllerhelpers.go | 0 .../internal/controllerutil/util.go | 0 .../internal/crypto/crypto.go | 0 .../internal/generator/generator.go | 0 .../internal/generator/password.go | 0 .../internal/generator/uuid.go | 0 .../{packages => internal}/model/model.go | 0 .../infisicaldynamicsecret/conditions.go | 2 +- .../infisicaldynamicsecret/handler.go | 110 + .../infisicaldynamicsecret/reconciler.go} | 68 +- .../infisicalpushsecret/conditions.go | 2 +- .../services/infisicalpushsecret/handler.go | 99 + .../infisicalpushsecret/reconciler.go} | 94 +- .../services/infisicalsecret/conditions.go | 0 .../services/infisicalsecret/handler.go | 0 .../services/infisicalsecret/reconciler.go | 0 .../services/infisicalsecret/suite_test.go | 0 .../internal/template/base64.go | 0 .../internal/template/jwk.go | 0 .../internal/template/pem.go | 0 .../internal/template/pem_chain.go | 0 .../internal/template/pkcs12.go | 0 .../internal/template/template.go | 0 .../internal/template/yaml.go | 0 .../{packages => internal}/util/auth.go | 0 .../internal/util/helpers.go | 0 .../internal/util/kubernetes.go | 0 .../{k8-operator => }/internal/util/models.go | 0 .../internal/util/secrets.go | 0 .../{k8-operator => }/internal/util/time.go | 0 .../internal/util/workspace.go | 0 .../.devcontainer/devcontainer.json | 25 - .../k8-operator/.devcontainer/post-install.sh | 23 - k8-operator/k8-operator/.dockerignore | 3 - .../k8-operator/.github/workflows/lint.yml | 23 - .../.github/workflows/test-e2e.yml | 32 - .../k8-operator/.github/workflows/test.yml | 23 - k8-operator/k8-operator/.gitignore | 27 - k8-operator/k8-operator/.golangci.yml | 52 - k8-operator/k8-operator/Dockerfile | 33 - k8-operator/k8-operator/Makefile | 238 -- k8-operator/k8-operator/PROJECT | 39 - k8-operator/k8-operator/README.md | 135 -- .../k8-operator/api/v1alpha1/common.go | 149 -- .../k8-operator/api/v1alpha1/generators.go | 152 -- .../api/v1alpha1/groupversion_info.go | 20 - .../v1alpha1/infisicaldynamicsecret_types.go | 99 - .../api/v1alpha1/infisicalpushsecret_types.go | 115 - .../api/v1alpha1/infisicalsecret_types.go | 182 -- .../api/v1alpha1/zz_generated.deepcopy.go | 307 --- ...crets.infisical.com_clustergenerators.yaml | 96 - ...infisical.com_infisicaldynamicsecrets.yaml | 309 --- ...ts.infisical.com_infisicalpushsecrets.yaml | 305 --- ...isical.com_infisicalpushsecretsecrets.yaml | 57 - ...ecrets.infisical.com_infisicalsecrets.yaml | 503 ---- .../secrets.infisical.com_passwords.yaml | 79 - .../bases/secrets.infisical.com_uuids.yaml | 46 - .../k8-operator/config/crd/kustomization.yaml | 18 - .../config/crd/kustomizeconfig.yaml | 19 - .../config/default/kustomization.yaml | 234 -- .../config/manager/kustomization.yaml | 2 - .../k8-operator/config/manager/manager.yaml | 99 - .../config/prometheus/kustomization.yaml | 11 - .../config/prometheus/monitor.yaml | 27 - .../infisicaldynamicsecret_editor_role.yaml | 33 - .../infisicaldynamicsecret_viewer_role.yaml | 29 - .../rbac/infisicalsecret_editor_role.yaml | 33 - .../rbac/infisicalsecret_viewer_role.yaml | 29 - .../config/rbac/kustomization.yaml | 34 - .../config/rbac/leader_election_role.yaml | 40 - .../rbac/leader_election_role_binding.yaml | 15 - k8-operator/k8-operator/config/rbac/role.yaml | 38 - .../k8-operator/config/rbac/role_binding.yaml | 15 - .../config/rbac/service_account.yaml | 8 - .../config/samples/kustomization.yaml | 6 - ...crets_v1alpha1_infisicaldynamicsecret.yaml | 9 - ...ts_v1alpha1_infisicalpushsecretsecret.yaml | 9 - .../secrets_v1alpha1_infisicalsecret.yaml | 9 - k8-operator/k8-operator/go.mod | 97 - k8-operator/k8-operator/go.sum | 254 -- .../k8-operator/hack/boilerplate.go.txt | 15 - .../infisicaldynamicsecret_controller.go | 63 - .../infisicalpushsecretsecret_controller.go | 63 - .../k8-operator/internal/model/model.go | 37 - k8-operator/k8-operator/internal/util/auth.go | 490 ---- .../install-secrets-operator.yaml | 2087 ++++++++--------- k8-operator/main.go | 137 -- k8-operator/packages/api/api.go | 148 -- k8-operator/packages/api/models.go | 208 -- k8-operator/packages/api/variables.go | 4 - k8-operator/packages/constants/constants.go | 42 - .../controllerhelpers/controllerhelpers.go | 293 --- k8-operator/packages/controllerutil/util.go | 45 - k8-operator/packages/crypto/crypto.go | 42 - k8-operator/packages/generator/generator.go | 1 - k8-operator/packages/generator/password.go | 76 - k8-operator/packages/generator/uuid.go | 10 - k8-operator/packages/template/base64.go | 18 - k8-operator/packages/template/jwk.go | 43 - k8-operator/packages/template/pem.go | 98 - k8-operator/packages/template/pem_chain.go | 117 - k8-operator/packages/template/pkcs12.go | 144 -- k8-operator/packages/template/template.go | 67 - k8-operator/packages/template/yaml.go | 30 - k8-operator/packages/util/helpers.go | 56 - k8-operator/packages/util/models.go | 13 - k8-operator/packages/util/secrets.go | 186 -- k8-operator/packages/util/time.go | 40 - k8-operator/packages/util/workspace.go | 27 - .../test/e2e/e2e_suite_test.go | 0 .../{k8-operator => }/test/e2e/e2e_test.go | 0 .../{k8-operator => }/test/utils/utils.go | 0 184 files changed, 2563 insertions(+), 9880 deletions(-) rename k8-operator/{k8-operator => }/cmd/main.go (96%) delete mode 100644 k8-operator/config/crd/patches/cainjection_in_infisicalsecrets.yaml delete mode 100644 k8-operator/config/crd/patches/webhook_in_infisicalsecrets.yaml rename k8-operator/{k8-operator => }/config/default/cert_metrics_manager_patch.yaml (100%) delete mode 100644 k8-operator/config/default/manager_auth_proxy_patch.yaml delete mode 100644 k8-operator/config/default/manager_config_patch.yaml rename k8-operator/{k8-operator => }/config/default/manager_metrics_patch.yaml (100%) rename k8-operator/{k8-operator => }/config/default/metrics_service.yaml (100%) rename k8-operator/{k8-operator => }/config/network-policy/allow-metrics-traffic.yaml (100%) rename k8-operator/{k8-operator => }/config/network-policy/kustomization.yaml (100%) rename k8-operator/{k8-operator => }/config/prometheus/monitor_tls_patch.yaml (100%) delete mode 100644 k8-operator/config/rbac/auth_proxy_client_clusterrole.yaml delete mode 100644 k8-operator/config/rbac/auth_proxy_role.yaml delete mode 100644 k8-operator/config/rbac/auth_proxy_role_binding.yaml delete mode 100644 k8-operator/config/rbac/auth_proxy_service.yaml rename k8-operator/{k8-operator => }/config/rbac/infisicaldynamicsecret_admin_role.yaml (100%) delete mode 100644 k8-operator/config/rbac/infisicalpushsecret_editor_role.yaml delete mode 100644 k8-operator/config/rbac/infisicalpushsecret_viewer_role.yaml rename k8-operator/{k8-operator => }/config/rbac/infisicalpushsecretsecret_admin_role.yaml (100%) rename k8-operator/{k8-operator => }/config/rbac/infisicalpushsecretsecret_editor_role.yaml (100%) rename k8-operator/{k8-operator => }/config/rbac/infisicalpushsecretsecret_viewer_role.yaml (100%) rename k8-operator/{k8-operator => }/config/rbac/infisicalsecret_admin_role.yaml (100%) rename k8-operator/{k8-operator => }/config/rbac/metrics_auth_role.yaml (100%) rename k8-operator/{k8-operator => }/config/rbac/metrics_auth_role_binding.yaml (100%) rename k8-operator/{k8-operator => }/config/rbac/metrics_reader_role.yaml (100%) delete mode 100644 k8-operator/controllers/infisicaldynamicsecret/infisicaldynamicsecret_controller.go delete mode 100644 k8-operator/controllers/infisicalsecret/conditions.go delete mode 100644 k8-operator/controllers/infisicalsecret/infisicalsecret_controller.go delete mode 100644 k8-operator/controllers/infisicalsecret/suite_test.go rename k8-operator/{k8-operator => }/internal/api/api.go (100%) rename k8-operator/{k8-operator => }/internal/api/models.go (100%) rename k8-operator/{k8-operator => }/internal/api/variables.go (100%) rename k8-operator/{k8-operator => }/internal/constants/constants.go (100%) rename k8-operator/{k8-operator => }/internal/controller/infisicaldynamicsecret_controller_test.go (100%) rename k8-operator/{controllers/infisicalpushsecret => internal/controller}/infisicalpushsecret_controller.go (76%) rename k8-operator/{k8-operator/internal/controller/infisicalpushsecretsecret_controller_test.go => internal/controller/infisicalpushsecret_controller_test.go} (92%) rename k8-operator/{k8-operator => }/internal/controller/infisicalsecret_controller.go (93%) rename k8-operator/{k8-operator => }/internal/controller/infisicalsecret_controller_test.go (100%) rename k8-operator/{k8-operator => }/internal/controller/suite_test.go (100%) rename k8-operator/{k8-operator => }/internal/controllerhelpers/controllerhelpers.go (100%) rename k8-operator/{k8-operator => }/internal/controllerutil/util.go (100%) rename k8-operator/{k8-operator => }/internal/crypto/crypto.go (100%) rename k8-operator/{k8-operator => }/internal/generator/generator.go (100%) rename k8-operator/{k8-operator => }/internal/generator/password.go (100%) rename k8-operator/{k8-operator => }/internal/generator/uuid.go (100%) rename k8-operator/{packages => internal}/model/model.go (100%) rename k8-operator/{controllers => internal/services}/infisicaldynamicsecret/conditions.go (99%) create mode 100644 k8-operator/internal/services/infisicaldynamicsecret/handler.go rename k8-operator/{controllers/infisicaldynamicsecret/infisicaldynamicsecret_helper.go => internal/services/infisicaldynamicsecret/reconciler.go} (87%) rename k8-operator/{controllers => internal/services}/infisicalpushsecret/conditions.go (99%) create mode 100644 k8-operator/internal/services/infisicalpushsecret/handler.go rename k8-operator/{controllers/infisicalpushsecret/infisicalpushsecret_helper.go => internal/services/infisicalpushsecret/reconciler.go} (85%) rename k8-operator/{k8-operator => }/internal/services/infisicalsecret/conditions.go (100%) rename k8-operator/{k8-operator => }/internal/services/infisicalsecret/handler.go (100%) rename k8-operator/{k8-operator => }/internal/services/infisicalsecret/reconciler.go (100%) rename k8-operator/{k8-operator => }/internal/services/infisicalsecret/suite_test.go (100%) rename k8-operator/{k8-operator => }/internal/template/base64.go (100%) rename k8-operator/{k8-operator => }/internal/template/jwk.go (100%) rename k8-operator/{k8-operator => }/internal/template/pem.go (100%) rename k8-operator/{k8-operator => }/internal/template/pem_chain.go (100%) rename k8-operator/{k8-operator => }/internal/template/pkcs12.go (100%) rename k8-operator/{k8-operator => }/internal/template/template.go (100%) rename k8-operator/{k8-operator => }/internal/template/yaml.go (100%) rename k8-operator/{packages => internal}/util/auth.go (100%) rename k8-operator/{k8-operator => }/internal/util/helpers.go (100%) rename k8-operator/{k8-operator => }/internal/util/kubernetes.go (100%) rename k8-operator/{k8-operator => }/internal/util/models.go (100%) rename k8-operator/{k8-operator => }/internal/util/secrets.go (100%) rename k8-operator/{k8-operator => }/internal/util/time.go (100%) rename k8-operator/{k8-operator => }/internal/util/workspace.go (100%) delete mode 100644 k8-operator/k8-operator/.devcontainer/devcontainer.json delete mode 100644 k8-operator/k8-operator/.devcontainer/post-install.sh delete mode 100644 k8-operator/k8-operator/.dockerignore delete mode 100644 k8-operator/k8-operator/.github/workflows/lint.yml delete mode 100644 k8-operator/k8-operator/.github/workflows/test-e2e.yml delete mode 100644 k8-operator/k8-operator/.github/workflows/test.yml delete mode 100644 k8-operator/k8-operator/.gitignore delete mode 100644 k8-operator/k8-operator/.golangci.yml delete mode 100644 k8-operator/k8-operator/Dockerfile delete mode 100644 k8-operator/k8-operator/Makefile delete mode 100644 k8-operator/k8-operator/PROJECT delete mode 100644 k8-operator/k8-operator/README.md delete mode 100644 k8-operator/k8-operator/api/v1alpha1/common.go delete mode 100644 k8-operator/k8-operator/api/v1alpha1/generators.go delete mode 100644 k8-operator/k8-operator/api/v1alpha1/groupversion_info.go delete mode 100644 k8-operator/k8-operator/api/v1alpha1/infisicaldynamicsecret_types.go delete mode 100644 k8-operator/k8-operator/api/v1alpha1/infisicalpushsecret_types.go delete mode 100644 k8-operator/k8-operator/api/v1alpha1/infisicalsecret_types.go delete mode 100644 k8-operator/k8-operator/api/v1alpha1/zz_generated.deepcopy.go delete mode 100644 k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_clustergenerators.yaml delete mode 100644 k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicaldynamicsecrets.yaml delete mode 100644 k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml delete mode 100644 k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecretsecrets.yaml delete mode 100644 k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml delete mode 100644 k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_passwords.yaml delete mode 100644 k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_uuids.yaml delete mode 100644 k8-operator/k8-operator/config/crd/kustomization.yaml delete mode 100644 k8-operator/k8-operator/config/crd/kustomizeconfig.yaml delete mode 100644 k8-operator/k8-operator/config/default/kustomization.yaml delete mode 100644 k8-operator/k8-operator/config/manager/kustomization.yaml delete mode 100644 k8-operator/k8-operator/config/manager/manager.yaml delete mode 100644 k8-operator/k8-operator/config/prometheus/kustomization.yaml delete mode 100644 k8-operator/k8-operator/config/prometheus/monitor.yaml delete mode 100644 k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_editor_role.yaml delete mode 100644 k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_viewer_role.yaml delete mode 100644 k8-operator/k8-operator/config/rbac/infisicalsecret_editor_role.yaml delete mode 100644 k8-operator/k8-operator/config/rbac/infisicalsecret_viewer_role.yaml delete mode 100644 k8-operator/k8-operator/config/rbac/kustomization.yaml delete mode 100644 k8-operator/k8-operator/config/rbac/leader_election_role.yaml delete mode 100644 k8-operator/k8-operator/config/rbac/leader_election_role_binding.yaml delete mode 100644 k8-operator/k8-operator/config/rbac/role.yaml delete mode 100644 k8-operator/k8-operator/config/rbac/role_binding.yaml delete mode 100644 k8-operator/k8-operator/config/rbac/service_account.yaml delete mode 100644 k8-operator/k8-operator/config/samples/kustomization.yaml delete mode 100644 k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicaldynamicsecret.yaml delete mode 100644 k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalpushsecretsecret.yaml delete mode 100644 k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalsecret.yaml delete mode 100644 k8-operator/k8-operator/go.mod delete mode 100644 k8-operator/k8-operator/go.sum delete mode 100644 k8-operator/k8-operator/hack/boilerplate.go.txt delete mode 100644 k8-operator/k8-operator/internal/controller/infisicaldynamicsecret_controller.go delete mode 100644 k8-operator/k8-operator/internal/controller/infisicalpushsecretsecret_controller.go delete mode 100644 k8-operator/k8-operator/internal/model/model.go delete mode 100644 k8-operator/k8-operator/internal/util/auth.go delete mode 100644 k8-operator/main.go delete mode 100644 k8-operator/packages/api/api.go delete mode 100644 k8-operator/packages/api/models.go delete mode 100644 k8-operator/packages/api/variables.go delete mode 100644 k8-operator/packages/constants/constants.go delete mode 100644 k8-operator/packages/controllerhelpers/controllerhelpers.go delete mode 100644 k8-operator/packages/controllerutil/util.go delete mode 100644 k8-operator/packages/crypto/crypto.go delete mode 100644 k8-operator/packages/generator/generator.go delete mode 100644 k8-operator/packages/generator/password.go delete mode 100644 k8-operator/packages/generator/uuid.go delete mode 100644 k8-operator/packages/template/base64.go delete mode 100644 k8-operator/packages/template/jwk.go delete mode 100644 k8-operator/packages/template/pem.go delete mode 100644 k8-operator/packages/template/pem_chain.go delete mode 100644 k8-operator/packages/template/pkcs12.go delete mode 100644 k8-operator/packages/template/template.go delete mode 100644 k8-operator/packages/template/yaml.go delete mode 100644 k8-operator/packages/util/helpers.go delete mode 100644 k8-operator/packages/util/models.go delete mode 100644 k8-operator/packages/util/secrets.go delete mode 100644 k8-operator/packages/util/time.go delete mode 100644 k8-operator/packages/util/workspace.go rename k8-operator/{k8-operator => }/test/e2e/e2e_suite_test.go (100%) rename k8-operator/{k8-operator => }/test/e2e/e2e_test.go (100%) rename k8-operator/{k8-operator => }/test/utils/utils.go (100%) diff --git a/k8-operator/Dockerfile b/k8-operator/Dockerfile index 2a69554f2..cb1b130fd 100644 --- a/k8-operator/Dockerfile +++ b/k8-operator/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.21 as builder +FROM golang:1.24 AS builder ARG TARGETOS ARG TARGETARCH @@ -12,17 +12,16 @@ COPY go.sum go.sum RUN go mod download # Copy the go source -COPY main.go main.go +COPY cmd/main.go cmd/main.go COPY api/ api/ -COPY controllers/ controllers/ -COPY packages/ packages/ +COPY internal/ internal/ # Build # the GOARCH has not a default value to allow the binary be built according to the host where the command # was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO # the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. -RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager main.go +RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go # Use distroless as minimal base image to package the manager binary # Refer to https://github.com/GoogleContainerTools/distroless for more details diff --git a/k8-operator/Makefile b/k8-operator/Makefile index 22fe5ef67..b776cf7a9 100644 --- a/k8-operator/Makefile +++ b/k8-operator/Makefile @@ -1,8 +1,5 @@ - # Image URL to use all building/pushing image targets -IMG ?= infisical/kubernetes-operator:latest -# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.25.0 +IMG ?= controller:latest # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -11,6 +8,12 @@ else GOBIN=$(shell go env GOBIN) endif +# CONTAINER_TOOL defines the container tool to be used for building images. +# Be aware that the target commands are only tested with Docker which is +# scaffolded by default. However, you might want to replace it to use other +# tools. (i.e. podman) +CONTAINER_TOOL ?= docker + # Setting SHELL to bash allows bash commands to be executed by recipes. # Options are set to exit when a recipe line exits non-zero or a piped command fails. SHELL = /usr/bin/env bash -o pipefail @@ -23,7 +26,7 @@ all: build # 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 +# target descriptions by '##'. The awk command 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. @@ -36,30 +39,6 @@ all: build help: ## Display this help. @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) - -# ## Chart - NOTE: change helper file to have 15 length for full name method -# helm-chart: -# $(KUSTOMIZE) build config/default | helmify ../helm-charts/secrets-operator - -HELMIFY ?= $(LOCALBIN)/helmify - -.PHONY: helmify -helmify: $(HELMIFY) ## Download helmify locally if necessary. -$(HELMIFY): $(LOCALBIN) - test -s $(LOCALBIN)/helmify || GOBIN=$(LOCALBIN) go install github.com/arttor/helmify/cmd/helmify@latest - -legacy-helm: manifests kustomize helmify - $(KUSTOMIZE) build config/default | $(HELMIFY) ../helm-charts/secrets-operator - -helm: manifests kustomize helmify - ./scripts/generate-helm.sh - -## Yaml for Kubectl -kubectl-install: manifests kustomize - mkdir -p kubectl-install - cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} - $(KUSTOMIZE) build config/default > kubectl-install/install-secrets-operator.yaml - ##@ Development .PHONY: manifests @@ -79,47 +58,94 @@ vet: ## Run go vet against code. go vet ./... .PHONY: test -test: manifests generate fmt vet envtest ## Run tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -coverprofile cover.out +test: manifests generate fmt vet setup-envtest ## Run tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out + +# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. +# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. +# CertManager is installed by default; skip with: +# - CERT_MANAGER_INSTALL_SKIP=true +KIND_CLUSTER ?= k8-operator-test-e2e + +.PHONY: setup-test-e2e +setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist + @command -v $(KIND) >/dev/null 2>&1 || { \ + echo "Kind is not installed. Please install Kind manually."; \ + exit 1; \ + } + @case "$$($(KIND) get clusters)" in \ + *"$(KIND_CLUSTER)"*) \ + echo "Kind cluster '$(KIND_CLUSTER)' already exists. Skipping creation." ;; \ + *) \ + echo "Creating Kind cluster '$(KIND_CLUSTER)'..."; \ + $(KIND) create cluster --name $(KIND_CLUSTER) ;; \ + esac + +.PHONY: test-e2e +test-e2e: setup-test-e2e manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind. + KIND_CLUSTER=$(KIND_CLUSTER) go test ./test/e2e/ -v -ginkgo.v + $(MAKE) cleanup-test-e2e + +.PHONY: cleanup-test-e2e +cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests + @$(KIND) delete cluster --name $(KIND_CLUSTER) + +.PHONY: lint +lint: golangci-lint ## Run golangci-lint linter + $(GOLANGCI_LINT) run + +.PHONY: lint-fix +lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes + $(GOLANGCI_LINT) run --fix + +.PHONY: lint-config +lint-config: golangci-lint ## Verify golangci-lint linter configuration + $(GOLANGCI_LINT) config verify ##@ Build .PHONY: build build: manifests generate fmt vet ## Build manager binary. - go build -o bin/manager main.go + go build -o bin/manager cmd/main.go .PHONY: run run: manifests generate fmt vet ## Run a controller from your host. - go run ./main.go + go run ./cmd/main.go -# If you wish built the manager image targeting other platforms you can use the --platform flag. -# (i.e. docker build --platform linux/arm64 ). However, you must enable docker buildKit for it. +# If you wish to build the manager image targeting other platforms you can use the --platform flag. +# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. # More info: https://docs.docker.com/develop/develop-images/build_enhancements/ .PHONY: docker-build -docker-build: test ## Build docker image with the manager. - docker build -t ${IMG} . +docker-build: ## Build docker image with the manager. + $(CONTAINER_TOOL) build -t ${IMG} . .PHONY: docker-push docker-push: ## Push docker image with the manager. - docker push ${IMG} + $(CONTAINER_TOOL) push ${IMG} -# PLATFORMS defines the target platforms for the manager image be build to provide support to multiple +# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple # architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: -# - able to use docker buildx . More info: https://docs.docker.com/build/buildx/ -# - have enable BuildKit, More info: https://docs.docker.com/develop/develop-images/build_enhancements/ -# - be able to push the image for your registry (i.e. if you do not inform a valid value via IMG=> then the export will fail) -# To properly provided solutions that supports more than one platform you should use this option. +# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/ +# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=> then the export will fail) +# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option. PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le .PHONY: docker-buildx -docker-buildx: test ## Build and push docker image for the manager for cross-platform support +docker-buildx: ## Build and push docker image for the manager for cross-platform support # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross - - docker buildx create --name project-v3-builder - docker buildx use project-v3-builder - - docker buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . - - docker buildx rm project-v3-builder + - $(CONTAINER_TOOL) buildx create --name k8-operator-builder + $(CONTAINER_TOOL) buildx use k8-operator-builder + - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . + - $(CONTAINER_TOOL) buildx rm k8-operator-builder rm Dockerfile.cross +.PHONY: build-installer +build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment. + mkdir -p dist + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default > dist/install.yaml + ##@ Deployment ifndef ignore-not-found @@ -128,22 +154,22 @@ endif .PHONY: install install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config. - $(KUSTOMIZE) build config/crd | kubectl apply -f - + $(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 - + $(KUSTOMIZE) build config/crd | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - .PHONY: deploy deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} - $(KUSTOMIZE) build config/default | kubectl apply -f - + $(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 - +undeploy: kustomize ## 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 - -##@ Build Dependencies +##@ Dependencies ## Location to install dependencies to LOCALBIN ?= $(shell pwd)/bin @@ -151,31 +177,62 @@ $(LOCALBIN): mkdir -p $(LOCALBIN) ## Tool Binaries +KUBECTL ?= kubectl +KIND ?= kind KUSTOMIZE ?= $(LOCALBIN)/kustomize CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest +GOLANGCI_LINT = $(LOCALBIN)/golangci-lint ## Tool Versions -KUSTOMIZE_VERSION ?= v3.8.7 -CONTROLLER_TOOLS_VERSION ?= v0.10.0 +KUSTOMIZE_VERSION ?= v5.6.0 +CONTROLLER_TOOLS_VERSION ?= v0.18.0 +#ENVTEST_VERSION is the version of controller-runtime release branch to fetch the envtest setup script (i.e. release-0.20) +ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}') +#ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31) +ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d", $$3}') +GOLANGCI_LINT_VERSION ?= v2.1.6 -KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" .PHONY: kustomize -kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading. +kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. $(KUSTOMIZE): $(LOCALBIN) - @if test -x $(LOCALBIN)/kustomize && ! $(LOCALBIN)/kustomize version | grep -q $(KUSTOMIZE_VERSION); then \ - echo "$(LOCALBIN)/kustomize version is not expected $(KUSTOMIZE_VERSION). Removing it before installing."; \ - rm -rf $(LOCALBIN)/kustomize; \ - fi - test -s $(LOCALBIN)/kustomize || { curl -Ss $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN); } + $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) .PHONY: controller-gen -controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. If wrong version is installed, it will be overwritten. +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. $(CONTROLLER_GEN): $(LOCALBIN) - test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || \ - GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) + $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) + +.PHONY: setup-envtest +setup-envtest: envtest ## Download the binaries required for ENVTEST in the local bin directory. + @echo "Setting up envtest binaries for Kubernetes version $(ENVTEST_K8S_VERSION)..." + @$(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path || { \ + echo "Error: Failed to set up envtest binaries for version $(ENVTEST_K8S_VERSION)."; \ + exit 1; \ + } .PHONY: envtest -envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. +envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. $(ENVTEST): $(LOCALBIN) - test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest + $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION)) + +.PHONY: golangci-lint +golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. +$(GOLANGCI_LINT): $(LOCALBIN) + $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) + +# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist +# $1 - target path with name of binary +# $2 - package url which can be installed +# $3 - specific version of package +define go-install-tool +@[ -f "$(1)-$(3)" ] || { \ +set -e; \ +package=$(2)@$(3) ;\ +echo "Downloading $${package}" ;\ +rm -f $(1) || true ;\ +GOBIN=$(LOCALBIN) go install $${package} ;\ +mv $(1) $(1)-$(3) ;\ +} ;\ +ln -sf $(1)-$(3) $(1) +endef diff --git a/k8-operator/PROJECT b/k8-operator/PROJECT index 59ebed6f6..dc9260f24 100644 --- a/k8-operator/PROJECT +++ b/k8-operator/PROJECT @@ -2,37 +2,38 @@ # 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 +cliVersion: 4.7.0 domain: infisical.com layout: - - go.kubebuilder.io/v3 +- go.kubebuilder.io/v4 projectName: k8-operator repo: github.com/Infisical/infisical/k8-operator resources: - - api: - crdVersion: v1 - namespaced: true - controller: true - domain: infisical.com - group: secrets - kind: InfisicalSecret - path: github.com/Infisical/infisical/k8-operator/api/v1alpha1 - version: v1alpha1 - - api: - crdVersion: v1 - namespaced: true - controller: true - domain: infisical.com - group: secrets - kind: InfisicalPushSecretSecret - path: github.com/Infisical/infisical/k8-operator/api/v1alpha1 - version: v1alpha1 - - api: - crdVersion: v1 - namespaced: true - controller: true - domain: infisical.com - group: secrets - kind: InfisicalDynamicSecret - path: github.com/Infisical/infisical/k8-operator/api/v1alpha1 - version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: infisical.com + group: secrets + kind: InfisicalSecret + path: github.com/Infisical/infisical/k8-operator/api/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: infisical.com + group: secrets + kind: InfisicalPushSecretSecret + path: github.com/Infisical/infisical/k8-operator/api/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: infisical.com + group: secrets + kind: InfisicalDynamicSecret + path: github.com/Infisical/infisical/k8-operator/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/k8-operator/api/v1alpha1/zz_generated.deepcopy.go b/k8-operator/api/v1alpha1/zz_generated.deepcopy.go index 4feafa4b0..f0ed9e216 100644 --- a/k8-operator/api/v1alpha1/zz_generated.deepcopy.go +++ b/k8-operator/api/v1alpha1/zz_generated.deepcopy.go @@ -1,8 +1,7 @@ //go:build !ignore_autogenerated -// +build !ignore_autogenerated /* -Copyright 2022. +Copyright 2025. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/k8-operator/k8-operator/cmd/main.go b/k8-operator/cmd/main.go similarity index 96% rename from k8-operator/k8-operator/cmd/main.go rename to k8-operator/cmd/main.go index 3e706edbe..1b71e0024 100644 --- a/k8-operator/k8-operator/cmd/main.go +++ b/k8-operator/cmd/main.go @@ -203,22 +203,25 @@ func main() { } if err := (&controller.InfisicalSecretReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + BaseLogger: ctrl.Log, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "InfisicalSecret") os.Exit(1) } - if err := (&controller.InfisicalPushSecretSecretReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + if err := (&controller.InfisicalPushSecretReconciler{ + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + BaseLogger: ctrl.Log, }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "InfisicalPushSecretSecret") + setupLog.Error(err, "unable to create controller", "controller", "InfisicalPushSecret") os.Exit(1) } if err := (&controller.InfisicalDynamicSecretReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + BaseLogger: ctrl.Log, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "InfisicalDynamicSecret") os.Exit(1) diff --git a/k8-operator/config/crd/bases/secrets.infisical.com_clustergenerators.yaml b/k8-operator/config/crd/bases/secrets.infisical.com_clustergenerators.yaml index c4a9eb168..0681f26ec 100644 --- a/k8-operator/config/crd/bases/secrets.infisical.com_clustergenerators.yaml +++ b/k8-operator/config/crd/bases/secrets.infisical.com_clustergenerators.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.18.0 name: clustergenerators.secrets.infisical.com spec: group: secrets.infisical.com @@ -21,14 +20,19 @@ spec: description: ClusterGenerator represents a cluster-wide generator 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' + 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' + 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 @@ -47,27 +51,29 @@ spec: description: set allowRepeat to true to allow repeating characters. type: boolean digits: - description: digits specifies the number of digits in the - generated password. If omitted it defaults to 25% of the - length of the password + description: |- + digits specifies the number of digits in the generated + password. If omitted it defaults to 25% of the length of the password type: integer length: default: 24 - description: Length of the password to be generated. Defaults - to 24 + description: |- + Length of the password to be generated. + Defaults to 24 type: integer noUpper: default: false description: Set noUpper to disable uppercase characters type: boolean symbolCharacters: - description: symbolCharacters specifies the special characters - that should be used in the generated password. + description: |- + symbolCharacters specifies the special characters that should be used + in the generated password. type: string symbols: - description: symbols specifies the number of symbol characters - in the generated password. If omitted it defaults to 25% - of the length of the password + description: |- + symbols specifies the number of symbol characters in the generated + password. If omitted it defaults to 25% of the length of the password type: integer type: object uuidSpec: diff --git a/k8-operator/config/crd/bases/secrets.infisical.com_infisicaldynamicsecrets.yaml b/k8-operator/config/crd/bases/secrets.infisical.com_infisicaldynamicsecrets.yaml index 2da712186..f50b303e9 100644 --- a/k8-operator/config/crd/bases/secrets.infisical.com_infisicaldynamicsecrets.yaml +++ b/k8-operator/config/crd/bases/secrets.infisical.com_infisicaldynamicsecrets.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.18.0 name: infisicaldynamicsecrets.secrets.infisical.com spec: group: secrets.infisical.com @@ -22,14 +21,19 @@ spec: 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' + 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' + 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 @@ -74,11 +78,9 @@ spec: kubernetesAuth: properties: autoCreateServiceAccountToken: - description: Optionally automatically create a service account - token for the configured service account. If this is set - to `true`, the operator will automatically create a service - account token for the configured service account. This field - is recommended in most cases. + description: |- + Optionally automatically create a service account token for the configured service account. + If this is set to `true`, the operator will automatically create a service account token for the configured service account. This field is recommended in most cases. type: boolean identityId: type: string @@ -169,12 +171,11 @@ spec: properties: creationPolicy: default: Orphan - description: 'The Kubernetes Secret creation policy. Enum with - values: ''Owner'', ''Orphan''. Owner creates the secret and - sets .metadata.ownerReferences of the InfisicalSecret CRD that - created it. Orphan will not set the secret owner. This will - result in the secret being orphaned and not deleted when the - resource is deleted.' + description: |- + The Kubernetes Secret creation policy. + Enum with values: 'Owner', 'Orphan'. + Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. + Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted. type: string secretName: description: The name of the Kubernetes Secret @@ -196,9 +197,9 @@ spec: description: The template key values type: object includeAllSecrets: - description: This injects all retrieved secrets into the top - level of your template. Secrets defined in the template - will take precedence over the injected ones. + description: |- + This injects all retrieved secrets into the top level of your template. + Secrets defined in the template will take precedence over the injected ones. type: boolean type: object required: @@ -240,43 +241,35 @@ spec: properties: conditions: items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 @@ -291,10 +284,6 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml b/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml index 1fc61e59f..a176e45f4 100644 --- a/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml +++ b/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.18.0 name: infisicalpushsecrets.secrets.infisical.com spec: group: secrets.infisical.com @@ -22,14 +21,19 @@ spec: 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' + 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' + 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 @@ -74,11 +78,9 @@ spec: kubernetesAuth: properties: autoCreateServiceAccountToken: - description: Optionally automatically create a service account - token for the configured service account. If this is set - to `true`, the operator will automatically create a service - account token for the configured service account. This field - is recommended in most cases. + description: |- + Optionally automatically create a service account token for the configured service account. + If this is set to `true`, the operator will automatically create a service account token for the configured service account. This field is recommended in most cases. type: boolean identityId: type: string @@ -208,9 +210,9 @@ spec: description: The template key values type: object includeAllSecrets: - description: This injects all retrieved secrets into the - top level of your template. Secrets defined in the template - will take precedence over the injected ones. + description: |- + This injects all retrieved secrets into the top level of your template. + Secrets defined in the template will take precedence over the injected ones. type: boolean type: object required: @@ -253,43 +255,35 @@ spec: properties: conditions: items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 @@ -304,10 +298,6 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml b/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml index 87cba3df0..3cf97dbaf 100644 --- a/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml +++ b/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.18.0 name: infisicalsecrets.secrets.infisical.com spec: group: secrets.infisical.com @@ -21,14 +20,19 @@ spec: description: InfisicalSecret is the Schema for the infisicalsecrets 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' + 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' + 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 @@ -137,10 +141,9 @@ spec: kubernetesAuth: properties: autoCreateServiceAccountToken: - description: Optionally automatically create a service account - token for the configured service account. If this is set - to `true`, the operator will automatically create a service - account token for the configured service account. + description: |- + Optionally automatically create a service account token for the configured service account. + If this is set to `true`, the operator will automatically create a service account token for the configured service account. type: boolean identityId: type: string @@ -323,12 +326,11 @@ spec: type: string creationPolicy: default: Orphan - description: 'The Kubernetes ConfigMap creation policy. Enum - with values: ''Owner'', ''Orphan''. Owner creates the config - map and sets .metadata.ownerReferences of the InfisicalSecret - CRD that created it. Orphan will not set the config map owner. - This will result in the config map being orphaned and not - deleted when the resource is deleted.' + description: |- + The Kubernetes ConfigMap creation policy. + Enum with values: 'Owner', 'Orphan'. + Owner creates the config map and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. + Orphan will not set the config map owner. This will result in the config map being orphaned and not deleted when the resource is deleted. type: string template: description: The template to transform the secret data @@ -339,9 +341,9 @@ spec: description: The template key values type: object includeAllSecrets: - description: This injects all retrieved secrets into the - top level of your template. Secrets defined in the template - will take precedence over the injected ones. + description: |- + This injects all retrieved secrets into the top level of your template. + Secrets defined in the template will take precedence over the injected ones. type: boolean type: object required: @@ -354,12 +356,11 @@ spec: properties: creationPolicy: default: Orphan - description: 'The Kubernetes Secret creation policy. Enum with - values: ''Owner'', ''Orphan''. Owner creates the secret and - sets .metadata.ownerReferences of the InfisicalSecret CRD - that created it. Orphan will not set the secret owner. This - will result in the secret being orphaned and not deleted when - the resource is deleted.' + description: |- + The Kubernetes Secret creation policy. + Enum with values: 'Owner', 'Orphan'. + Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. + Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted. type: string secretName: description: The name of the Kubernetes Secret @@ -381,9 +382,9 @@ spec: description: The template key values type: object includeAllSecrets: - description: This injects all retrieved secrets into the - top level of your template. Secrets defined in the template - will take precedence over the injected ones. + description: |- + This injects all retrieved secrets into the top level of your template. + Secrets defined in the template will take precedence over the injected ones. type: boolean type: object required: @@ -395,12 +396,11 @@ spec: properties: creationPolicy: default: Orphan - description: 'The Kubernetes Secret creation policy. Enum with - values: ''Owner'', ''Orphan''. Owner creates the secret and - sets .metadata.ownerReferences of the InfisicalSecret CRD that - created it. Orphan will not set the secret owner. This will - result in the secret being orphaned and not deleted when the - resource is deleted.' + description: |- + The Kubernetes Secret creation policy. + Enum with values: 'Owner', 'Orphan'. + Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. + Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted. type: string secretName: description: The name of the Kubernetes Secret @@ -422,9 +422,9 @@ spec: description: The template key values type: object includeAllSecrets: - description: This injects all retrieved secrets into the top - level of your template. Secrets defined in the template - will take precedence over the injected ones. + description: |- + This injects all retrieved secrets into the top level of your template. + Secrets defined in the template will take precedence over the injected ones. type: boolean type: object required: @@ -476,43 +476,35 @@ spec: properties: conditions: items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. This field may not be empty. maxLength: 1024 minLength: 1 @@ -527,10 +519,6 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/k8-operator/config/crd/bases/secrets.infisical.com_passwords.yaml b/k8-operator/config/crd/bases/secrets.infisical.com_passwords.yaml index dc14f2bf0..788e077a6 100644 --- a/k8-operator/config/crd/bases/secrets.infisical.com_passwords.yaml +++ b/k8-operator/config/crd/bases/secrets.infisical.com_passwords.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.18.0 name: passwords.secrets.infisical.com spec: group: secrets.infisical.com @@ -18,18 +17,25 @@ spec: - name: v1alpha1 schema: openAPIV3Schema: - description: Password generates a random password based on the configuration - parameters in spec. You can specify the length, characterset and other attributes. + description: |- + Password generates a random password based on the + configuration parameters in spec. + You can specify the length, characterset and other attributes. 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' + 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' + 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 @@ -41,25 +47,29 @@ spec: description: set allowRepeat to true to allow repeating characters. type: boolean digits: - description: digits specifies the number of digits in the generated + description: |- + digits specifies the number of digits in the generated password. If omitted it defaults to 25% of the length of the password type: integer length: default: 24 - description: Length of the password to be generated. Defaults to 24 + description: |- + Length of the password to be generated. + Defaults to 24 type: integer noUpper: default: false description: Set noUpper to disable uppercase characters type: boolean symbolCharacters: - description: symbolCharacters specifies the special characters that - should be used in the generated password. + description: |- + symbolCharacters specifies the special characters that should be used + in the generated password. type: string symbols: - description: symbols specifies the number of symbol characters in - the generated password. If omitted it defaults to 25% of the length - of the password + description: |- + symbols specifies the number of symbol characters in the generated + password. If omitted it defaults to 25% of the length of the password type: integer type: object type: object diff --git a/k8-operator/config/crd/bases/secrets.infisical.com_uuids.yaml b/k8-operator/config/crd/bases/secrets.infisical.com_uuids.yaml index 495b5b276..659dfc7ac 100644 --- a/k8-operator/config/crd/bases/secrets.infisical.com_uuids.yaml +++ b/k8-operator/config/crd/bases/secrets.infisical.com_uuids.yaml @@ -3,8 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.10.0 - creationTimestamp: null + controller-gen.kubebuilder.io/version: v0.18.0 name: uuids.secrets.infisical.com spec: group: secrets.infisical.com @@ -21,14 +20,19 @@ spec: description: UUID generates a version 4 UUID (e56657e3-764f-11ef-a397-65231a88c216). 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' + 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' + 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 diff --git a/k8-operator/config/crd/patches/cainjection_in_infisicalsecrets.yaml b/k8-operator/config/crd/patches/cainjection_in_infisicalsecrets.yaml deleted file mode 100644 index 79efe831a..000000000 --- a/k8-operator/config/crd/patches/cainjection_in_infisicalsecrets.yaml +++ /dev/null @@ -1,7 +0,0 @@ -# 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: infisicalsecrets.secrets.infisical.com diff --git a/k8-operator/config/crd/patches/webhook_in_infisicalsecrets.yaml b/k8-operator/config/crd/patches/webhook_in_infisicalsecrets.yaml deleted file mode 100644 index 706d26708..000000000 --- a/k8-operator/config/crd/patches/webhook_in_infisicalsecrets.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# The following patch enables a conversion webhook for the CRD -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - name: infisicalsecrets.secrets.infisical.com -spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - namespace: system - name: webhook-service - path: /convert - conversionReviewVersions: - - v1 diff --git a/k8-operator/k8-operator/config/default/cert_metrics_manager_patch.yaml b/k8-operator/config/default/cert_metrics_manager_patch.yaml similarity index 100% rename from k8-operator/k8-operator/config/default/cert_metrics_manager_patch.yaml rename to k8-operator/config/default/cert_metrics_manager_patch.yaml diff --git a/k8-operator/config/default/kustomization.yaml b/k8-operator/config/default/kustomization.yaml index 1237b893d..8eda77014 100644 --- a/k8-operator/config/default/kustomization.yaml +++ b/k8-operator/config/default/kustomization.yaml @@ -1,18 +1,20 @@ # Adds namespace to all resources. -namespace: infisical-operator-system +namespace: k8-operator-system # Value of this field is prepended to the # names of all resources, e.g. a deployment named # "wordpress" becomes "alices-wordpress". # Note that it should also match with the prefix (text before '-') of the namespace # field above. -namePrefix: infisical-operator- +namePrefix: k8-operator- # Labels to add to all resources and selectors. -#commonLabels: -# someName: someValue +#labels: +#- includeSelectors: true +# pairs: +# someName: someValue -bases: +resources: - ../crd - ../rbac - ../manager @@ -23,50 +25,210 @@ bases: #- ../certmanager # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. #- ../prometheus +# [METRICS] Expose the controller manager metrics service. +- metrics_service.yaml +# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy. +# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics. +# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will +# be able to communicate with the Webhook Server. +#- ../network-policy -patchesStrategicMerge: -# Protect the /metrics endpoint by putting it behind auth. -# If you want your controller-manager to expose the /metrics -# endpoint w/o any authn/z, please comment the following line. -- manager_auth_proxy_patch.yaml - +# Uncomment the patches line if you enable Metrics +patches: +# [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. +# More info: https://book.kubebuilder.io/reference/metrics +- path: manager_metrics_patch.yaml + target: + kind: Deployment +# Uncomment the patches line if you enable Metrics and CertManager +# [METRICS-WITH-CERTS] To enable metrics protected with certManager, uncomment the following line. +# This patch will protect the metrics with certManager self-signed certs. +#- path: cert_metrics_manager_patch.yaml +# target: +# kind: Deployment # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml -#- manager_webhook_patch.yaml +#- path: manager_webhook_patch.yaml +# target: +# kind: Deployment -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. -# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. -# 'CERTMANAGER' needs to be enabled to use ca injection -#- webhookcainjection_patch.yaml - -# the following config is for teaching kustomize how to do var substitution -vars: # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -#- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldref: -# fieldpath: metadata.namespace -#- name: CERTIFICATE_NAME -# objref: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -#- name: SERVICE_NAMESPACE # namespace of the service -# objref: -# kind: Service -# version: v1 -# name: webhook-service -# fieldref: -# fieldpath: metadata.namespace -#- name: SERVICE_NAME -# objref: -# kind: Service -# version: v1 -# name: webhook-service +# Uncomment the following replacements to add the cert-manager CA injection annotations +#replacements: +# - source: # Uncomment the following block to enable certificates for metrics +# kind: Service +# version: v1 +# name: controller-manager-metrics-service +# fieldPath: metadata.name +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: metrics-certs +# fieldPaths: +# - spec.dnsNames.0 +# - spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - select: # Uncomment the following to set the Service name for TLS config in Prometheus ServiceMonitor +# kind: ServiceMonitor +# group: monitoring.coreos.com +# version: v1 +# name: controller-manager-metrics-monitor +# fieldPaths: +# - spec.endpoints.0.tlsConfig.serverName +# options: +# delimiter: '.' +# index: 0 +# create: true + +# - source: +# kind: Service +# version: v1 +# name: controller-manager-metrics-service +# fieldPath: metadata.namespace +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: metrics-certs +# fieldPaths: +# - spec.dnsNames.0 +# - spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true +# - select: # Uncomment the following to set the Service namespace for TLS in Prometheus ServiceMonitor +# kind: ServiceMonitor +# group: monitoring.coreos.com +# version: v1 +# name: controller-manager-metrics-monitor +# fieldPaths: +# - spec.endpoints.0.tlsConfig.serverName +# options: +# delimiter: '.' +# index: 1 +# create: true + +# - source: # Uncomment the following block if you have any webhook +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.name # Name of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - source: +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.namespace # Namespace of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true + +# - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # This name should match the one in certificate.yaml +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true + +# - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting ) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true + +# - source: # Uncomment the following block if you have a ConversionWebhook (--conversion) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionns +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionname diff --git a/k8-operator/config/default/manager_auth_proxy_patch.yaml b/k8-operator/config/default/manager_auth_proxy_patch.yaml deleted file mode 100644 index d413370bf..000000000 --- a/k8-operator/config/default/manager_auth_proxy_patch.yaml +++ /dev/null @@ -1,55 +0,0 @@ -# This patch inject a sidecar container which is a HTTP proxy for the -# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/arch - operator: In - values: - - amd64 - - arm64 - - ppc64le - - s390x - - key: kubernetes.io/os - operator: In - values: - - linux - containers: - - name: kube-rbac-proxy - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - "ALL" - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.15.0 - args: - - "--secure-listen-address=0.0.0.0:8443" - - "--upstream=http://127.0.0.1:8080/" - - "--logtostderr=true" - - "--v=0" - ports: - - containerPort: 8443 - protocol: TCP - name: https - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - - name: manager - args: - - "--health-probe-bind-address=:8081" - - "--metrics-bind-address=127.0.0.1:8080" - - "--leader-elect" diff --git a/k8-operator/config/default/manager_config_patch.yaml b/k8-operator/config/default/manager_config_patch.yaml deleted file mode 100644 index f6f589169..000000000 --- a/k8-operator/config/default/manager_config_patch.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: manager diff --git a/k8-operator/k8-operator/config/default/manager_metrics_patch.yaml b/k8-operator/config/default/manager_metrics_patch.yaml similarity index 100% rename from k8-operator/k8-operator/config/default/manager_metrics_patch.yaml rename to k8-operator/config/default/manager_metrics_patch.yaml diff --git a/k8-operator/k8-operator/config/default/metrics_service.yaml b/k8-operator/config/default/metrics_service.yaml similarity index 100% rename from k8-operator/k8-operator/config/default/metrics_service.yaml rename to k8-operator/config/default/metrics_service.yaml diff --git a/k8-operator/config/manager/kustomization.yaml b/k8-operator/config/manager/kustomization.yaml index 96ea36924..5c5f0b84c 100644 --- a/k8-operator/config/manager/kustomization.yaml +++ b/k8-operator/config/manager/kustomization.yaml @@ -1,8 +1,2 @@ resources: - manager.yaml -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -images: -- name: controller - newName: infisical/kubernetes-operator - newTag: latest diff --git a/k8-operator/config/manager/manager.yaml b/k8-operator/config/manager/manager.yaml index 60ba38105..eb41eff84 100644 --- a/k8-operator/config/manager/manager.yaml +++ b/k8-operator/config/manager/manager.yaml @@ -3,11 +3,7 @@ kind: Namespace metadata: labels: control-plane: controller-manager - app.kubernetes.io/name: namespace - app.kubernetes.io/instance: system - app.kubernetes.io/component: manager - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator + app.kubernetes.io/name: k8-operator app.kubernetes.io/managed-by: kustomize name: system --- @@ -18,16 +14,13 @@ metadata: namespace: system labels: control-plane: controller-manager - app.kubernetes.io/name: deployment - app.kubernetes.io/instance: controller-manager - app.kubernetes.io/component: manager - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator + app.kubernetes.io/name: k8-operator app.kubernetes.io/managed-by: kustomize spec: selector: matchLabels: control-plane: controller-manager + app.kubernetes.io/name: k8-operator replicas: 1 template: metadata: @@ -35,6 +28,7 @@ spec: kubectl.kubernetes.io/default-container: manager labels: control-plane: controller-manager + app.kubernetes.io/name: k8-operator spec: # TODO(user): Uncomment the following code to configure the nodeAffinity expression # according to the platforms which are supported by your solution. @@ -57,26 +51,27 @@ spec: # values: # - linux securityContext: + # Projects are configured by default to adhere to the "restricted" Pod Security Standards. + # This ensures that deployments meet the highest security requirements for Kubernetes. + # For more details, see: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted runAsNonRoot: true - # TODO(user): For common cases that do not require escalating privileges - # it is recommended to ensure that all your Pods/Containers are restrictive. - # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted - # Please uncomment the following code if your project does NOT have to work on old Kubernetes - # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). - # seccompProfile: - # type: RuntimeDefault + seccompProfile: + type: RuntimeDefault containers: - command: - /manager args: - - --leader-elect + - --leader-elect + - --health-probe-bind-address=:8081 image: controller:latest name: manager + ports: [] securityContext: + readOnlyRootFilesystem: true allowPrivilegeEscalation: false capabilities: drop: - - "ALL" + - "ALL" livenessProbe: httpGet: path: /healthz @@ -98,5 +93,7 @@ spec: requests: cpu: 10m memory: 64Mi + volumeMounts: [] + volumes: [] serviceAccountName: controller-manager terminationGracePeriodSeconds: 10 diff --git a/k8-operator/k8-operator/config/network-policy/allow-metrics-traffic.yaml b/k8-operator/config/network-policy/allow-metrics-traffic.yaml similarity index 100% rename from k8-operator/k8-operator/config/network-policy/allow-metrics-traffic.yaml rename to k8-operator/config/network-policy/allow-metrics-traffic.yaml diff --git a/k8-operator/k8-operator/config/network-policy/kustomization.yaml b/k8-operator/config/network-policy/kustomization.yaml similarity index 100% rename from k8-operator/k8-operator/config/network-policy/kustomization.yaml rename to k8-operator/config/network-policy/kustomization.yaml diff --git a/k8-operator/config/prometheus/kustomization.yaml b/k8-operator/config/prometheus/kustomization.yaml index ed137168a..fdc5481b1 100644 --- a/k8-operator/config/prometheus/kustomization.yaml +++ b/k8-operator/config/prometheus/kustomization.yaml @@ -1,2 +1,11 @@ resources: - monitor.yaml + +# [PROMETHEUS-WITH-CERTS] The following patch configures the ServiceMonitor in ../prometheus +# to securely reference certificates created and managed by cert-manager. +# Additionally, ensure that you uncomment the [METRICS WITH CERTMANAGER] patch under config/default/kustomization.yaml +# to mount the "metrics-server-cert" secret in the Manager Deployment. +#patches: +# - path: monitor_tls_patch.yaml +# target: +# kind: ServiceMonitor diff --git a/k8-operator/config/prometheus/monitor.yaml b/k8-operator/config/prometheus/monitor.yaml index 2f3526185..abaef6489 100644 --- a/k8-operator/config/prometheus/monitor.yaml +++ b/k8-operator/config/prometheus/monitor.yaml @@ -1,26 +1,27 @@ - # Prometheus Monitor Service (Metrics) apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: labels: control-plane: controller-manager - app.kubernetes.io/name: servicemonitor - app.kubernetes.io/instance: controller-manager-metrics-monitor - app.kubernetes.io/component: metrics - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator + app.kubernetes.io/name: k8-operator app.kubernetes.io/managed-by: kustomize name: controller-manager-metrics-monitor namespace: system spec: endpoints: - path: /metrics - port: https + port: https # Ensure this is the name of the port that exposes HTTPS metrics scheme: https bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token tlsConfig: + # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables + # certificate verification, exposing the system to potential man-in-the-middle attacks. + # For production environments, it is recommended to use cert-manager for automatic TLS certificate management. + # To apply this configuration, enable cert-manager and use the patch located at config/prometheus/servicemonitor_tls_patch.yaml, + # which securely references the certificate from the 'metrics-server-cert' secret. insecureSkipVerify: true selector: matchLabels: control-plane: controller-manager + app.kubernetes.io/name: k8-operator diff --git a/k8-operator/k8-operator/config/prometheus/monitor_tls_patch.yaml b/k8-operator/config/prometheus/monitor_tls_patch.yaml similarity index 100% rename from k8-operator/k8-operator/config/prometheus/monitor_tls_patch.yaml rename to k8-operator/config/prometheus/monitor_tls_patch.yaml diff --git a/k8-operator/config/rbac/auth_proxy_client_clusterrole.yaml b/k8-operator/config/rbac/auth_proxy_client_clusterrole.yaml deleted file mode 100644 index fc7ce7735..000000000 --- a/k8-operator/config/rbac/auth_proxy_client_clusterrole.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: metrics-reader - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator - app.kubernetes.io/managed-by: kustomize - name: metrics-reader -rules: -- nonResourceURLs: - - "/metrics" - verbs: - - get diff --git a/k8-operator/config/rbac/auth_proxy_role.yaml b/k8-operator/config/rbac/auth_proxy_role.yaml deleted file mode 100644 index 7b0469d27..000000000 --- a/k8-operator/config/rbac/auth_proxy_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: proxy-role - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator - app.kubernetes.io/managed-by: kustomize - name: proxy-role -rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create diff --git a/k8-operator/config/rbac/auth_proxy_role_binding.yaml b/k8-operator/config/rbac/auth_proxy_role_binding.yaml deleted file mode 100644 index 8d9a7c035..000000000 --- a/k8-operator/config/rbac/auth_proxy_role_binding.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/name: clusterrolebinding - app.kubernetes.io/instance: proxy-rolebinding - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator - app.kubernetes.io/managed-by: kustomize - name: proxy-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: proxy-role -subjects: -- kind: ServiceAccount - name: controller-manager - namespace: system diff --git a/k8-operator/config/rbac/auth_proxy_service.yaml b/k8-operator/config/rbac/auth_proxy_service.yaml deleted file mode 100644 index 5a1c43b6d..000000000 --- a/k8-operator/config/rbac/auth_proxy_service.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - control-plane: controller-manager - app.kubernetes.io/name: service - app.kubernetes.io/instance: controller-manager-metrics-service - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator - app.kubernetes.io/managed-by: kustomize - name: controller-manager-metrics-service - namespace: system -spec: - ports: - - name: https - port: 8443 - protocol: TCP - targetPort: https - selector: - control-plane: controller-manager diff --git a/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_admin_role.yaml b/k8-operator/config/rbac/infisicaldynamicsecret_admin_role.yaml similarity index 100% rename from k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_admin_role.yaml rename to k8-operator/config/rbac/infisicaldynamicsecret_admin_role.yaml diff --git a/k8-operator/config/rbac/infisicaldynamicsecret_editor_role.yaml b/k8-operator/config/rbac/infisicaldynamicsecret_editor_role.yaml index 9d68cdc75..4d3ffc985 100644 --- a/k8-operator/config/rbac/infisicaldynamicsecret_editor_role.yaml +++ b/k8-operator/config/rbac/infisicaldynamicsecret_editor_role.yaml @@ -1,4 +1,10 @@ -# permissions for end users to edit infisicaldynamicsecrets. +# This rule is not used by the project k8-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the secrets.infisical.com. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: diff --git a/k8-operator/config/rbac/infisicaldynamicsecret_viewer_role.yaml b/k8-operator/config/rbac/infisicaldynamicsecret_viewer_role.yaml index b80f51fa6..d5cfc7be9 100644 --- a/k8-operator/config/rbac/infisicaldynamicsecret_viewer_role.yaml +++ b/k8-operator/config/rbac/infisicaldynamicsecret_viewer_role.yaml @@ -1,4 +1,10 @@ -# permissions for end users to view infisicaldynamicsecrets. +# This rule is not used by the project k8-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to secrets.infisical.com resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: diff --git a/k8-operator/config/rbac/infisicalpushsecret_editor_role.yaml b/k8-operator/config/rbac/infisicalpushsecret_editor_role.yaml deleted file mode 100644 index 9344e17c5..000000000 --- a/k8-operator/config/rbac/infisicalpushsecret_editor_role.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# permissions for end users to edit infisicalpushsecrets. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize - name: infisicalpushsecret-editor-role -rules: - - apiGroups: - - secrets.infisical.com - resources: - - infisicalpushsecrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch - - apiGroups: - - secrets.infisical.com - resources: - - infisicalpushsecrets/status - verbs: - - get diff --git a/k8-operator/config/rbac/infisicalpushsecret_viewer_role.yaml b/k8-operator/config/rbac/infisicalpushsecret_viewer_role.yaml deleted file mode 100644 index ff4df91cc..000000000 --- a/k8-operator/config/rbac/infisicalpushsecret_viewer_role.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# permissions for end users to view infisicalpushsecrets. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize - name: infisicalpushsecret-viewer-role -rules: - - apiGroups: - - secrets.infisical.com - resources: - - infisicalpushsecrets - verbs: - - get - - list - - watch - - apiGroups: - - secrets.infisical.com - resources: - - infisicalpushsecrets/status - verbs: - - get diff --git a/k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_admin_role.yaml b/k8-operator/config/rbac/infisicalpushsecretsecret_admin_role.yaml similarity index 100% rename from k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_admin_role.yaml rename to k8-operator/config/rbac/infisicalpushsecretsecret_admin_role.yaml diff --git a/k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_editor_role.yaml b/k8-operator/config/rbac/infisicalpushsecretsecret_editor_role.yaml similarity index 100% rename from k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_editor_role.yaml rename to k8-operator/config/rbac/infisicalpushsecretsecret_editor_role.yaml diff --git a/k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_viewer_role.yaml b/k8-operator/config/rbac/infisicalpushsecretsecret_viewer_role.yaml similarity index 100% rename from k8-operator/k8-operator/config/rbac/infisicalpushsecretsecret_viewer_role.yaml rename to k8-operator/config/rbac/infisicalpushsecretsecret_viewer_role.yaml diff --git a/k8-operator/k8-operator/config/rbac/infisicalsecret_admin_role.yaml b/k8-operator/config/rbac/infisicalsecret_admin_role.yaml similarity index 100% rename from k8-operator/k8-operator/config/rbac/infisicalsecret_admin_role.yaml rename to k8-operator/config/rbac/infisicalsecret_admin_role.yaml diff --git a/k8-operator/config/rbac/infisicalsecret_editor_role.yaml b/k8-operator/config/rbac/infisicalsecret_editor_role.yaml index 7107057b5..a70abf3c4 100644 --- a/k8-operator/config/rbac/infisicalsecret_editor_role.yaml +++ b/k8-operator/config/rbac/infisicalsecret_editor_role.yaml @@ -1,13 +1,15 @@ -# permissions for end users to edit infisicalsecrets. +# This rule is not used by the project k8-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants permissions to create, update, and delete resources within the secrets.infisical.com. +# This role is intended for users who need to manage these resources +# but should not control RBAC or manage permissions for others. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: - app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: infisicalsecret-editor-role - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator + app.kubernetes.io/name: k8-operator app.kubernetes.io/managed-by: kustomize name: infisicalsecret-editor-role rules: diff --git a/k8-operator/config/rbac/infisicalsecret_viewer_role.yaml b/k8-operator/config/rbac/infisicalsecret_viewer_role.yaml index ead9de98a..a5a724940 100644 --- a/k8-operator/config/rbac/infisicalsecret_viewer_role.yaml +++ b/k8-operator/config/rbac/infisicalsecret_viewer_role.yaml @@ -1,13 +1,15 @@ -# permissions for end users to view infisicalsecrets. +# This rule is not used by the project k8-operator itself. +# It is provided to allow the cluster admin to help manage permissions for users. +# +# Grants read-only access to secrets.infisical.com resources. +# This role is intended for users who need visibility into these resources +# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. + apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: labels: - app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: infisicalsecret-viewer-role - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator + app.kubernetes.io/name: k8-operator app.kubernetes.io/managed-by: kustomize name: infisicalsecret-viewer-role rules: diff --git a/k8-operator/config/rbac/kustomization.yaml b/k8-operator/config/rbac/kustomization.yaml index 731832a6a..d879dffa4 100644 --- a/k8-operator/config/rbac/kustomization.yaml +++ b/k8-operator/config/rbac/kustomization.yaml @@ -9,10 +9,26 @@ resources: - role_binding.yaml - leader_election_role.yaml - leader_election_role_binding.yaml -# Comment the following 4 lines if you want to disable -# the auth proxy (https://github.com/brancz/kube-rbac-proxy) -# which protects your /metrics endpoint. -- auth_proxy_service.yaml -- auth_proxy_role.yaml -- auth_proxy_role_binding.yaml -- auth_proxy_client_clusterrole.yaml +# The following RBAC configurations are used to protect +# the metrics endpoint with authn/authz. These configurations +# ensure that only authorized users and service accounts +# can access the metrics endpoint. Comment the following +# permissions if you want to disable this protection. +# More info: https://book.kubebuilder.io/reference/metrics.html +- metrics_auth_role.yaml +- metrics_auth_role_binding.yaml +- metrics_reader_role.yaml +# For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by +# default, aiding admins in cluster management. Those roles are +# not used by the k8-operator itself. You can comment the following lines +# if you do not want those helpers be installed with your Project. +- infisicaldynamicsecret_admin_role.yaml +- infisicaldynamicsecret_editor_role.yaml +- infisicaldynamicsecret_viewer_role.yaml +- infisicalpushsecretsecret_admin_role.yaml +- infisicalpushsecretsecret_editor_role.yaml +- infisicalpushsecretsecret_viewer_role.yaml +- infisicalsecret_admin_role.yaml +- infisicalsecret_editor_role.yaml +- infisicalsecret_viewer_role.yaml + diff --git a/k8-operator/config/rbac/leader_election_role.yaml b/k8-operator/config/rbac/leader_election_role.yaml index d1174d650..a86e00ed1 100644 --- a/k8-operator/config/rbac/leader_election_role.yaml +++ b/k8-operator/config/rbac/leader_election_role.yaml @@ -3,11 +3,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: labels: - app.kubernetes.io/name: role - app.kubernetes.io/instance: leader-election-role - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator + app.kubernetes.io/name: k8-operator app.kubernetes.io/managed-by: kustomize name: leader-election-role rules: diff --git a/k8-operator/config/rbac/leader_election_role_binding.yaml b/k8-operator/config/rbac/leader_election_role_binding.yaml index 5202c011f..d662fc9de 100644 --- a/k8-operator/config/rbac/leader_election_role_binding.yaml +++ b/k8-operator/config/rbac/leader_election_role_binding.yaml @@ -2,11 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: - app.kubernetes.io/name: rolebinding - app.kubernetes.io/instance: leader-election-rolebinding - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator + app.kubernetes.io/name: k8-operator app.kubernetes.io/managed-by: kustomize name: leader-election-rolebinding roleRef: diff --git a/k8-operator/k8-operator/config/rbac/metrics_auth_role.yaml b/k8-operator/config/rbac/metrics_auth_role.yaml similarity index 100% rename from k8-operator/k8-operator/config/rbac/metrics_auth_role.yaml rename to k8-operator/config/rbac/metrics_auth_role.yaml diff --git a/k8-operator/k8-operator/config/rbac/metrics_auth_role_binding.yaml b/k8-operator/config/rbac/metrics_auth_role_binding.yaml similarity index 100% rename from k8-operator/k8-operator/config/rbac/metrics_auth_role_binding.yaml rename to k8-operator/config/rbac/metrics_auth_role_binding.yaml diff --git a/k8-operator/k8-operator/config/rbac/metrics_reader_role.yaml b/k8-operator/config/rbac/metrics_reader_role.yaml similarity index 100% rename from k8-operator/k8-operator/config/rbac/metrics_reader_role.yaml rename to k8-operator/config/rbac/metrics_reader_role.yaml diff --git a/k8-operator/config/rbac/role.yaml b/k8-operator/config/rbac/role.yaml index ea2fbada3..67216ba4f 100644 --- a/k8-operator/config/rbac/role.yaml +++ b/k8-operator/config/rbac/role.yaml @@ -2,13 +2,13 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - creationTimestamp: null name: manager-role rules: - apiGroups: - "" resources: - configmaps + - secrets verbs: - create - delete @@ -23,17 +23,6 @@ rules: verbs: - get - list -- apiGroups: - - "" - resources: - - secrets - verbs: - - create - - delete - - get - - list - - update - - watch - apiGroups: - "" resources: @@ -48,17 +37,6 @@ rules: - serviceaccounts/token verbs: - create -- apiGroups: - - apps - resources: - - daemonsets - - deployments - - statefulsets - verbs: - - get - - list - - update - - watch - apiGroups: - apps resources: @@ -78,69 +56,8 @@ rules: - secrets.infisical.com resources: - clustergenerators - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - secrets.infisical.com - resources: - infisicaldynamicsecrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - secrets.infisical.com - resources: - - infisicaldynamicsecrets/finalizers - verbs: - - update -- apiGroups: - - secrets.infisical.com - resources: - - infisicaldynamicsecrets/status - verbs: - - get - - patch - - update -- apiGroups: - - secrets.infisical.com - resources: - infisicalpushsecrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - secrets.infisical.com - resources: - - infisicalpushsecrets/finalizers - verbs: - - update -- apiGroups: - - secrets.infisical.com - resources: - - infisicalpushsecrets/status - verbs: - - get - - patch - - update -- apiGroups: - - secrets.infisical.com - resources: - infisicalsecrets verbs: - create @@ -153,12 +70,16 @@ rules: - apiGroups: - secrets.infisical.com resources: + - infisicaldynamicsecrets/finalizers + - infisicalpushsecrets/finalizers - infisicalsecrets/finalizers verbs: - update - apiGroups: - secrets.infisical.com resources: + - infisicaldynamicsecrets/status + - infisicalpushsecrets/status - infisicalsecrets/status verbs: - get diff --git a/k8-operator/config/rbac/role_binding.yaml b/k8-operator/config/rbac/role_binding.yaml index 62aee486d..5e15ad6f4 100644 --- a/k8-operator/config/rbac/role_binding.yaml +++ b/k8-operator/config/rbac/role_binding.yaml @@ -2,11 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: labels: - app.kubernetes.io/name: clusterrolebinding - app.kubernetes.io/instance: manager-rolebinding - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator + app.kubernetes.io/name: k8-operator app.kubernetes.io/managed-by: kustomize name: manager-rolebinding roleRef: diff --git a/k8-operator/config/rbac/service_account.yaml b/k8-operator/config/rbac/service_account.yaml index da689a67d..ed238fe99 100644 --- a/k8-operator/config/rbac/service_account.yaml +++ b/k8-operator/config/rbac/service_account.yaml @@ -2,11 +2,7 @@ apiVersion: v1 kind: ServiceAccount metadata: labels: - app.kubernetes.io/name: serviceaccount - app.kubernetes.io/instance: controller-manager - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator + app.kubernetes.io/name: k8-operator app.kubernetes.io/managed-by: kustomize name: controller-manager namespace: system diff --git a/k8-operator/config/samples/crd/infisicaldynamicsecret/dynamicSecret.yaml b/k8-operator/config/samples/crd/infisicaldynamicsecret/dynamicSecret.yaml index b34259927..98bb54866 100644 --- a/k8-operator/config/samples/crd/infisicaldynamicsecret/dynamicSecret.yaml +++ b/k8-operator/config/samples/crd/infisicaldynamicsecret/dynamicSecret.yaml @@ -3,13 +3,13 @@ kind: InfisicalDynamicSecret metadata: name: infisicaldynamicsecret-demo spec: - hostAPI: https://app.infisical.com/api + hostAPI: http://localhost:8080/api dynamicSecret: - secretName: - projectId: - secretsPath: - environmentSlug: + secretName: dynamic-secret + projectId: 5cdc4fec-f541-413c-b0bc-4c15572e421e + secretsPath: / + environmentSlug: dev leaseRevocationPolicy: Revoke # Revoke or None. Revoke will revoke leases created by the operator if the CRD is deleted. leaseTTL: 1m # TTL for the leases created. Must be below 24 hours. diff --git a/k8-operator/config/samples/crd/infisicalsecret/infisicalSecretCrd.yaml b/k8-operator/config/samples/crd/infisicalsecret/infisicalSecretCrd.yaml index f5209e71c..b18f5df82 100644 --- a/k8-operator/config/samples/crd/infisicalsecret/infisicalSecretCrd.yaml +++ b/k8-operator/config/samples/crd/infisicalsecret/infisicalSecretCrd.yaml @@ -7,7 +7,7 @@ metadata: annotations: example.com/annotation-to-be-passed-to-managed-secret: "sample-value" spec: - hostAPI: https://app.infisical.com/api + hostAPI: http://localhost:8080/api resyncInterval: 10 # tls: # caRef: @@ -15,23 +15,10 @@ spec: # secretNamespace: default # key: ca.crt authentication: - # Make sure to only have 1 authentication method defined, serviceToken/universalAuth. - # If you have multiple authentication methods defined, it may cause issues. - - # (Deprecated) Service Token Auth - serviceToken: - serviceTokenSecretReference: - secretName: service-token - secretNamespace: default - secretsScope: - envSlug: - secretsPath: - recursive: true - # Universal Auth universalAuth: secretsScope: - projectSlug: new-ob-em + projectSlug: hello-9zkr envSlug: dev # "dev", "staging", "prod", etc.. secretsPath: "/" # Root is "/" recursive: true # Wether or not to use recursive mode (Fetches all secrets in an environment from a given secret path, and all folders inside the path) / defaults to false @@ -117,9 +104,3 @@ spec: - secretName: managed-secret secretNamespace: default creationPolicy: "Orphan" ## Owner | Orphan - # secretType: kubernetes.io/dockerconfigjson - - # # To be depreciated soon - # tokenSecretReference: - # secretName: service-token - # secretNamespace: default diff --git a/k8-operator/config/samples/crd/pushsecret/push-secret.yaml b/k8-operator/config/samples/crd/pushsecret/push-secret.yaml index b6a132987..15302e6cc 100644 --- a/k8-operator/config/samples/crd/pushsecret/push-secret.yaml +++ b/k8-operator/config/samples/crd/pushsecret/push-secret.yaml @@ -4,7 +4,7 @@ metadata: name: infisical-api-secret-sample-push spec: resyncInterval: 1m - hostAPI: https://app.infisical.com/api + hostAPI: http://localhost:8080/api # Optional, defaults to replacement. updatePolicy: Replace # If set to replace, existing secrets inside Infisical will be replaced by the value of the PushSecret on sync. @@ -13,9 +13,9 @@ spec: deletionPolicy: Delete # If set to delete, the secret(s) inside Infisical managed by the operator, will be deleted if the InfisicalPushSecret CRD is deleted. destination: - projectId: - environmentSlug: - secretsPath: + projectId: 5cdc4fec-f541-413c-b0bc-4c15572e421e + environmentSlug: dev + secretsPath: / push: secret: @@ -25,21 +25,7 @@ spec: # Only have one authentication method defined or you are likely to run into authentication issues. # Remove all except one authentication method. authentication: - awsIamAuth: - identityId: - azureAuth: - identityId: - gcpIamAuth: - identityId: - serviceAccountKeyFilePath: - gcpIdTokenAuth: - identityId: - kubernetesAuth: - identityId: - serviceAccountRef: - name: - namespace: universalAuth: credentialsRef: - secretName: # universal-auth-credentials - secretNamespace: # default + secretName: universal-auth-credentials # universal-auth-credentials + secretNamespace: default # default diff --git a/k8-operator/config/samples/universalAuthIdentitySecret.yaml b/k8-operator/config/samples/universalAuthIdentitySecret.yaml index c2a69b438..88d60e6ab 100644 --- a/k8-operator/config/samples/universalAuthIdentitySecret.yaml +++ b/k8-operator/config/samples/universalAuthIdentitySecret.yaml @@ -1,8 +1,8 @@ apiVersion: v1 kind: Secret metadata: - name: universal-auth-credentials + name: universal-auth-credentials type: Opaque stringData: - clientId: - clientSecret: + clientId: da81e27e-1885-47d9-9ea3-ec7d4d807bb6 + clientSecret: 2772414d440fe04d8b975f5fe25acd0fbfe71b2a4a420409eb9ac6f5ae6c1e98 diff --git a/k8-operator/controllers/infisicaldynamicsecret/infisicaldynamicsecret_controller.go b/k8-operator/controllers/infisicaldynamicsecret/infisicaldynamicsecret_controller.go deleted file mode 100644 index a65676739..000000000 --- a/k8-operator/controllers/infisicaldynamicsecret/infisicaldynamicsecret_controller.go +++ /dev/null @@ -1,217 +0,0 @@ -package controllers - -import ( - "context" - "fmt" - "math/rand" - "time" - - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" - - secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" - "github.com/Infisical/infisical/k8-operator/packages/api" - "github.com/Infisical/infisical/k8-operator/packages/constants" - controllerhelpers "github.com/Infisical/infisical/k8-operator/packages/controllerhelpers" - "github.com/Infisical/infisical/k8-operator/packages/util" - "github.com/go-logr/logr" -) - -// InfisicalDynamicSecretReconciler reconciles a InfisicalDynamicSecret object -type InfisicalDynamicSecretReconciler struct { - client.Client - Scheme *runtime.Scheme - - BaseLogger logr.Logger - Random *rand.Rand -} - -var infisicalDynamicSecretsResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables) - -func (r *InfisicalDynamicSecretReconciler) GetLogger(req ctrl.Request) logr.Logger { - return r.BaseLogger.WithValues("infisicaldynamicsecret", req.NamespacedName) -} - -// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/status,verbs=get;update;patch -// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/finalizers,verbs=update -// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;delete -// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;delete -// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=list;watch;get;update -// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch -//+kubebuilder:rbac:groups="",resources=pods,verbs=get;list -//+kubebuilder:rbac:groups="authentication.k8s.io",resources=tokenreviews,verbs=create -//+kubebuilder:rbac:groups="",resources=serviceaccounts/token,verbs=create - -func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - - logger := r.GetLogger(req) - - var infisicalDynamicSecretCRD secretsv1alpha1.InfisicalDynamicSecret - requeueTime := time.Second * 5 - - err := r.Get(ctx, req.NamespacedName, &infisicalDynamicSecretCRD) - if err != nil { - if errors.IsNotFound(err) { - logger.Info("Infisical Dynamic Secret CRD not found") - return ctrl.Result{ - Requeue: false, - }, nil - } else { - logger.Error(err, "Unable to fetch Infisical Dynamic Secret CRD from cluster") - return ctrl.Result{ - RequeueAfter: requeueTime, - }, nil - } - } - - // Add finalizer if it doesn't exist - if !controllerutil.ContainsFinalizer(&infisicalDynamicSecretCRD, constants.INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME) { - controllerutil.AddFinalizer(&infisicalDynamicSecretCRD, constants.INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME) - if err := r.Update(ctx, &infisicalDynamicSecretCRD); err != nil { - return ctrl.Result{}, err - } - } - - // Check if it's being deleted - if !infisicalDynamicSecretCRD.DeletionTimestamp.IsZero() { - logger.Info("Handling deletion of InfisicalDynamicSecret") - if controllerutil.ContainsFinalizer(&infisicalDynamicSecretCRD, constants.INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME) { - // We remove finalizers before running deletion logic to be completely safe from stuck resources - infisicalDynamicSecretCRD.ObjectMeta.Finalizers = []string{} - if err := r.Update(ctx, &infisicalDynamicSecretCRD); err != nil { - logger.Error(err, fmt.Sprintf("Error removing finalizers from InfisicalDynamicSecret %s", infisicalDynamicSecretCRD.Name)) - return ctrl.Result{}, err - } - - err := r.HandleLeaseRevocation(ctx, logger, &infisicalDynamicSecretCRD) - - if infisicalDynamicSecretsResourceVariablesMap != nil { - if rv, ok := infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecretCRD.GetUID())]; ok { - rv.CancelCtx() - delete(infisicalDynamicSecretsResourceVariablesMap, string(infisicalDynamicSecretCRD.GetUID())) - } - } - - if err != nil { - return ctrl.Result{}, err // Even if this fails, we still want to delete the CRD - } - - } - return ctrl.Result{}, nil - } - - // Get modified/default config - infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client) - if err != nil { - logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime)) - return ctrl.Result{ - RequeueAfter: requeueTime, - }, nil - } - - if infisicalDynamicSecretCRD.Spec.HostAPI == "" { - api.API_HOST_URL = infisicalConfig["hostAPI"] - } else { - api.API_HOST_URL = util.AppendAPIEndpoint(infisicalDynamicSecretCRD.Spec.HostAPI) - } - - if infisicalDynamicSecretCRD.Spec.TLS.CaRef.SecretName != "" { - api.API_CA_CERTIFICATE, err = r.getInfisicalCaCertificateFromKubeSecret(ctx, infisicalDynamicSecretCRD) - if err != nil { - logger.Error(err, fmt.Sprintf("unable to fetch CA certificate. Will requeue after [requeueTime=%v]", requeueTime)) - return ctrl.Result{ - RequeueAfter: requeueTime, - }, nil - } - - logger.Info("Using custom CA certificate...") - } else { - api.API_CA_CERTIFICATE = "" - } - - nextReconcile, err := r.ReconcileInfisicalDynamicSecret(ctx, logger, &infisicalDynamicSecretCRD) - r.SetReconcileConditionStatus(ctx, logger, &infisicalDynamicSecretCRD, err) - - if err == nil && nextReconcile.Seconds() >= 5 { - requeueTime = nextReconcile - } - - if err != nil { - logger.Error(err, fmt.Sprintf("unable to reconcile Infisical Push Secret. Will requeue after [requeueTime=%v]", requeueTime)) - return ctrl.Result{ - RequeueAfter: requeueTime, - }, nil - } - - numDeployments, err := controllerhelpers.ReconcileDeploymentsWithManagedSecrets(ctx, r.Client, logger, infisicalDynamicSecretCRD.Spec.ManagedSecretReference) - r.SetReconcileAutoRedeploymentConditionStatus(ctx, logger, &infisicalDynamicSecretCRD, numDeployments, err) - - if err != nil { - logger.Error(err, fmt.Sprintf("unable to reconcile auto redeployment. Will requeue after [requeueTime=%v]", requeueTime)) - return ctrl.Result{ - RequeueAfter: requeueTime, - }, nil - } - - // Sync again after the specified time - logger.Info(fmt.Sprintf("Next reconciliation in [requeueTime=%v]", requeueTime)) - return ctrl.Result{ - RequeueAfter: requeueTime, - }, nil -} - -func (r *InfisicalDynamicSecretReconciler) SetupWithManager(mgr ctrl.Manager) error { - - // Custom predicate that allows both spec changes and deletions - specChangeOrDelete := predicate.Funcs{ - UpdateFunc: func(e event.UpdateEvent) bool { - // Only reconcile if spec/generation changed - - isSpecOrGenerationChange := e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration() - - if isSpecOrGenerationChange { - if infisicalDynamicSecretsResourceVariablesMap != nil { - if rv, ok := infisicalDynamicSecretsResourceVariablesMap[string(e.ObjectNew.GetUID())]; ok { - rv.CancelCtx() - delete(infisicalDynamicSecretsResourceVariablesMap, string(e.ObjectNew.GetUID())) - } - } - } - - return isSpecOrGenerationChange - }, - DeleteFunc: func(e event.DeleteEvent) bool { - // Always reconcile on deletion - - if infisicalDynamicSecretsResourceVariablesMap != nil { - if rv, ok := infisicalDynamicSecretsResourceVariablesMap[string(e.Object.GetUID())]; ok { - rv.CancelCtx() - delete(infisicalDynamicSecretsResourceVariablesMap, string(e.Object.GetUID())) - } - } - - return true - }, - CreateFunc: func(e event.CreateEvent) bool { - // Reconcile on creation - return true - }, - GenericFunc: func(e event.GenericEvent) bool { - // Ignore generic events - return false - }, - } - - return ctrl.NewControllerManagedBy(mgr). - For(&secretsv1alpha1.InfisicalDynamicSecret{}, builder.WithPredicates( - specChangeOrDelete, - )). - Complete(r) -} diff --git a/k8-operator/controllers/infisicalsecret/conditions.go b/k8-operator/controllers/infisicalsecret/conditions.go deleted file mode 100644 index b9d09d5e2..000000000 --- a/k8-operator/controllers/infisicalsecret/conditions.go +++ /dev/null @@ -1,100 +0,0 @@ -package controllers - -import ( - "context" - "fmt" - - "github.com/Infisical/infisical/k8-operator/api/v1alpha1" - "github.com/Infisical/infisical/k8-operator/packages/util" - "github.com/go-logr/logr" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func (r *InfisicalSecretReconciler) SetReadyToSyncSecretsConditions(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, secretsCount int, errorToConditionOn error) { - if infisicalSecret.Status.Conditions == nil { - infisicalSecret.Status.Conditions = []metav1.Condition{} - } - - if errorToConditionOn != nil { - meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{ - Type: "secrets.infisical.com/ReadyToSyncSecrets", - Status: metav1.ConditionFalse, - Reason: "Error", - Message: fmt.Sprintf("Failed to sync secrets. This can be caused by invalid access token or an invalid API host that is set. Error: %v", errorToConditionOn), - }) - - meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{ - Type: "secrets.infisical.com/AutoRedeployReady", - Status: metav1.ConditionFalse, - Reason: "Stopped", - Message: fmt.Sprintf("Auto redeployment has been stopped because the operator failed to sync secrets. Error: %v", errorToConditionOn), - }) - } else { - meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{ - Type: "secrets.infisical.com/ReadyToSyncSecrets", - Status: metav1.ConditionTrue, - Reason: "OK", - Message: fmt.Sprintf("Infisical controller has started syncing your secrets. Last reconcile synced %d secrets", secretsCount), - }) - } - - err := r.Client.Status().Update(ctx, infisicalSecret) - if err != nil { - logger.Error(err, "Could not set condition for ReadyToSyncSecrets") - } -} - -func (r *InfisicalSecretReconciler) SetInfisicalTokenLoadCondition(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, authStrategy util.AuthStrategyType, errorToConditionOn error) { - if infisicalSecret.Status.Conditions == nil { - infisicalSecret.Status.Conditions = []metav1.Condition{} - } - - if errorToConditionOn == nil { - meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{ - Type: "secrets.infisical.com/LoadedInfisicalToken", - Status: metav1.ConditionTrue, - Reason: "OK", - Message: fmt.Sprintf("Infisical controller has loaded the Infisical token in provided Kubernetes secret, using %v authentication strategy", authStrategy), - }) - } else { - meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{ - Type: "secrets.infisical.com/LoadedInfisicalToken", - Status: metav1.ConditionFalse, - Reason: "Error", - Message: fmt.Sprintf("Failed to load Infisical Token from the provided Kubernetes secret because: %v", errorToConditionOn), - }) - } - - err := r.Client.Status().Update(ctx, infisicalSecret) - if err != nil { - logger.Error(err, "Could not set condition for LoadedInfisicalToken") - } -} - -func (r *InfisicalSecretReconciler) SetInfisicalAutoRedeploymentReady(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, numDeployments int, errorToConditionOn error) { - if infisicalSecret.Status.Conditions == nil { - infisicalSecret.Status.Conditions = []metav1.Condition{} - } - - if errorToConditionOn == nil { - meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{ - Type: "secrets.infisical.com/AutoRedeployReady", - Status: metav1.ConditionTrue, - Reason: "OK", - Message: fmt.Sprintf("Infisical has found %v deployments which are ready to be auto redeployed when secrets change", numDeployments), - }) - } else { - meta.SetStatusCondition(&infisicalSecret.Status.Conditions, metav1.Condition{ - Type: "secrets.infisical.com/AutoRedeployReady", - Status: metav1.ConditionFalse, - Reason: "Error", - Message: fmt.Sprintf("Failed reconcile deployments because: %v", errorToConditionOn), - }) - } - - err := r.Client.Status().Update(ctx, infisicalSecret) - if err != nil { - logger.Error(err, "Could not set condition for AutoRedeployReady") - } -} diff --git a/k8-operator/controllers/infisicalsecret/infisicalsecret_controller.go b/k8-operator/controllers/infisicalsecret/infisicalsecret_controller.go deleted file mode 100644 index bf7d75830..000000000 --- a/k8-operator/controllers/infisicalsecret/infisicalsecret_controller.go +++ /dev/null @@ -1,212 +0,0 @@ -package controllers - -import ( - "context" - "fmt" - "time" - - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/builder" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/event" - "sigs.k8s.io/controller-runtime/pkg/predicate" - - defaultErrors "errors" - - secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" - "github.com/Infisical/infisical/k8-operator/packages/api" - controllerhelpers "github.com/Infisical/infisical/k8-operator/packages/controllerhelpers" - "github.com/Infisical/infisical/k8-operator/packages/util" - "github.com/go-logr/logr" -) - -// InfisicalSecretReconciler reconciles a InfisicalSecret object -type InfisicalSecretReconciler struct { - client.Client - BaseLogger logr.Logger - Scheme *runtime.Scheme -} - -const FINALIZER_NAME = "secrets.finalizers.infisical.com" - -var infisicalSecretResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables) - -func (r *InfisicalSecretReconciler) GetLogger(req ctrl.Request) logr.Logger { - return r.BaseLogger.WithValues("infisicalsecret", req.NamespacedName) -} - -//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/finalizers,verbs=update -//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;delete -//+kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;delete -//+kubebuilder:rbac:groups=apps,resources=deployments;daemonsets;statefulsets,verbs=list;watch;get;update -//+kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch -//+kubebuilder:rbac:groups="",resources=pods,verbs=get;list -//+kubebuilder:rbac:groups="authentication.k8s.io",resources=tokenreviews,verbs=create -//+kubebuilder:rbac:groups="",resources=serviceaccounts/token,verbs=create - -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.1/pkg/reconcile - -func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - - logger := r.GetLogger(req) - - var infisicalSecretCRD secretsv1alpha1.InfisicalSecret - requeueTime := time.Minute // seconds - - err := r.Get(ctx, req.NamespacedName, &infisicalSecretCRD) - if err != nil { - if errors.IsNotFound(err) { - return ctrl.Result{ - Requeue: false, - }, nil - } else { - logger.Error(err, "unable to fetch Infisical Secret CRD from cluster") - return ctrl.Result{ - RequeueAfter: requeueTime, - }, nil - } - } - - // It's important we don't directly modify the CRD object, so we create a copy of it and move existing data into it. - managedKubeSecretReferences := infisicalSecretCRD.Spec.ManagedKubeSecretReferences - managedKubeConfigMapReferences := infisicalSecretCRD.Spec.ManagedKubeConfigMapReferences - - if infisicalSecretCRD.Spec.ManagedSecretReference.SecretName != "" && managedKubeSecretReferences != nil && len(managedKubeSecretReferences) > 0 { - errMessage := "InfisicalSecret CRD cannot have both managedSecretReference and managedKubeSecretReferences" - logger.Error(defaultErrors.New(errMessage), errMessage) - return ctrl.Result{}, defaultErrors.New(errMessage) - } - - if infisicalSecretCRD.Spec.ManagedSecretReference.SecretName != "" { - logger.Info("\n\n\nThe field `managedSecretReference` will be deprecated in the near future, please use `managedKubeSecretReferences` instead.\n\nRefer to the documentation for more information: https://infisical.com/docs/integrations/platforms/kubernetes/infisical-secret-crd\n\n\n") - - if managedKubeSecretReferences == nil { - managedKubeSecretReferences = []secretsv1alpha1.ManagedKubeSecretConfig{} - } - managedKubeSecretReferences = append(managedKubeSecretReferences, infisicalSecretCRD.Spec.ManagedSecretReference) - } - - if len(managedKubeSecretReferences) == 0 && len(managedKubeConfigMapReferences) == 0 { - errMessage := "InfisicalSecret CRD must have at least one managed secret reference set in the `managedKubeSecretReferences` or `managedKubeConfigMapReferences` field" - logger.Error(defaultErrors.New(errMessage), errMessage) - return ctrl.Result{}, defaultErrors.New(errMessage) - } - - // Remove finalizers if they exist. This is to support previous InfisicalSecret CRD's that have finalizers on them. - // In order to delete secrets with finalizers, we first remove the finalizers so we can use the simplified and improved deletion process - if !infisicalSecretCRD.ObjectMeta.DeletionTimestamp.IsZero() && len(infisicalSecretCRD.ObjectMeta.Finalizers) > 0 { - infisicalSecretCRD.ObjectMeta.Finalizers = []string{} - if err := r.Update(ctx, &infisicalSecretCRD); err != nil { - logger.Error(err, fmt.Sprintf("Error removing finalizers from Infisical Secret %s", infisicalSecretCRD.Name)) - return ctrl.Result{}, err - } - // Our finalizers have been removed, so the reconciler can do nothing. - return ctrl.Result{}, nil - } - - if infisicalSecretCRD.Spec.ResyncInterval != 0 { - requeueTime = time.Second * time.Duration(infisicalSecretCRD.Spec.ResyncInterval) - logger.Info(fmt.Sprintf("Manual re-sync interval set. Interval: %v", requeueTime)) - - } else { - logger.Info(fmt.Sprintf("Re-sync interval set. Interval: %v", requeueTime)) - } - - // Check if the resource is already marked for deletion - if infisicalSecretCRD.GetDeletionTimestamp() != nil { - return ctrl.Result{ - Requeue: false, - }, nil - } - - // Get modified/default config - infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client) - if err != nil { - logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime)) - return ctrl.Result{ - RequeueAfter: requeueTime, - }, nil - } - - if infisicalSecretCRD.Spec.HostAPI == "" { - api.API_HOST_URL = infisicalConfig["hostAPI"] - } else { - api.API_HOST_URL = util.AppendAPIEndpoint(infisicalSecretCRD.Spec.HostAPI) - } - - if infisicalSecretCRD.Spec.TLS.CaRef.SecretName != "" { - api.API_CA_CERTIFICATE, err = r.getInfisicalCaCertificateFromKubeSecret(ctx, infisicalSecretCRD) - if err != nil { - logger.Error(err, fmt.Sprintf("unable to fetch CA certificate. Will requeue after [requeueTime=%v]", requeueTime)) - return ctrl.Result{ - RequeueAfter: requeueTime, - }, nil - } - - logger.Info("Using custom CA certificate...") - } else { - api.API_CA_CERTIFICATE = "" - } - - secretsCount, err := r.ReconcileInfisicalSecret(ctx, logger, &infisicalSecretCRD, managedKubeSecretReferences, managedKubeConfigMapReferences) - r.SetReadyToSyncSecretsConditions(ctx, logger, &infisicalSecretCRD, secretsCount, err) - - if err != nil { - logger.Error(err, fmt.Sprintf("unable to reconcile InfisicalSecret. Will requeue after [requeueTime=%v]", requeueTime)) - return ctrl.Result{ - RequeueAfter: requeueTime, - }, nil - } - - numDeployments, err := controllerhelpers.ReconcileDeploymentsWithMultipleManagedSecrets(ctx, r.Client, logger, managedKubeSecretReferences) - r.SetInfisicalAutoRedeploymentReady(ctx, logger, &infisicalSecretCRD, numDeployments, err) - - if err != nil { - logger.Error(err, fmt.Sprintf("unable to reconcile auto redeployment. Will requeue after [requeueTime=%v]", requeueTime)) - return ctrl.Result{ - RequeueAfter: requeueTime, - }, nil - } - - // Sync again after the specified time - logger.Info(fmt.Sprintf("Successfully synced %d secrets. Operator will requeue after [%v]", secretsCount, requeueTime)) - return ctrl.Result{ - RequeueAfter: requeueTime, - }, nil -} - -func (r *InfisicalSecretReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&secretsv1alpha1.InfisicalSecret{}, builder.WithPredicates(predicate.Funcs{ - UpdateFunc: func(e event.UpdateEvent) bool { - if e.ObjectOld.GetGeneration() == e.ObjectNew.GetGeneration() { - return false // Skip reconciliation for status-only changes - } - - if infisicalSecretResourceVariablesMap != nil { - if rv, ok := infisicalSecretResourceVariablesMap[string(e.ObjectNew.GetUID())]; ok { - rv.CancelCtx() - delete(infisicalSecretResourceVariablesMap, string(e.ObjectNew.GetUID())) - } - } - return true - }, - DeleteFunc: func(e event.DeleteEvent) bool { - if infisicalSecretResourceVariablesMap != nil { - if rv, ok := infisicalSecretResourceVariablesMap[string(e.Object.GetUID())]; ok { - rv.CancelCtx() - delete(infisicalSecretResourceVariablesMap, string(e.Object.GetUID())) - } - } - return true - }, - })). - Complete(r) -} diff --git a/k8-operator/controllers/infisicalsecret/suite_test.go b/k8-operator/controllers/infisicalsecret/suite_test.go deleted file mode 100644 index bd46b22e0..000000000 --- a/k8-operator/controllers/infisicalsecret/suite_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package controllers - -import ( - "path/filepath" - "testing" - - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - - secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" - //+kubebuilder:scaffold:imports -) - -// These tests use Ginkgo (BDD-style Go testing framework). Refer to -// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. - -var cfg *rest.Config -var k8sClient client.Client -var testEnv *envtest.Environment - -func TestAPIs(t *testing.T) { - RegisterFailHandler(Fail) - - RunSpecs(t, "Controller Suite") -} - -var _ = BeforeSuite(func() { - logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) - - By("bootstrapping test environment") - testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, - ErrorIfCRDPathMissing: true, - } - - var err error - // cfg is defined in this file globally. - cfg, err = testEnv.Start() - Expect(err).NotTo(HaveOccurred()) - Expect(cfg).NotTo(BeNil()) - - err = secretsv1alpha1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - - //+kubebuilder:scaffold:scheme - - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - Expect(err).NotTo(HaveOccurred()) - Expect(k8sClient).NotTo(BeNil()) - -}) - -var _ = AfterSuite(func() { - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).NotTo(HaveOccurred()) -}) diff --git a/k8-operator/go.mod b/k8-operator/go.mod index 782059503..a80b8839f 100644 --- a/k8-operator/go.mod +++ b/k8-operator/go.mod @@ -1,133 +1,150 @@ module github.com/Infisical/infisical/k8-operator -go 1.21 +go 1.24.0 require ( github.com/Masterminds/sprig/v3 v3.3.0 - github.com/aws/smithy-go v1.20.3 + github.com/aws/smithy-go v1.22.4 + github.com/go-logr/logr v1.4.2 + github.com/go-resty/resty/v2 v2.16.5 + github.com/google/uuid v1.6.0 github.com/infisical/go-sdk v0.5.99 - github.com/lestrrat-go/jwx/v2 v2.1.4 - github.com/onsi/ginkgo/v2 v2.6.0 - github.com/onsi/gomega v1.24.1 + github.com/lestrrat-go/jwx/v2 v2.1.6 + github.com/onsi/ginkgo/v2 v2.22.0 + github.com/onsi/gomega v1.36.1 github.com/sethvargo/go-password v0.3.1 - k8s.io/apimachinery v0.26.1 - k8s.io/client-go v0.26.1 - sigs.k8s.io/controller-runtime v0.14.4 - software.sslmate.com/src/go-pkcs12 v0.5.0 + golang.org/x/crypto v0.36.0 + gopkg.in/yaml.v3 v3.0.1 + k8s.io/api v0.33.0 + k8s.io/apimachinery v0.33.0 + k8s.io/client-go v0.33.0 + sigs.k8s.io/controller-runtime v0.21.0 + software.sslmate.com/src/go-pkcs12 v0.6.0 ) require ( + cel.dev/expr v0.19.1 // indirect cloud.google.com/go/auth v0.7.0 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect - cloud.google.com/go/compute/metadata v0.4.0 // indirect + cloud.google.com/go/compute/metadata v0.5.0 // indirect cloud.google.com/go/iam v1.1.11 // indirect dario.cat/mergo v1.0.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.3.0 // indirect - github.com/aws/aws-sdk-go-v2 v1.30.1 // indirect - github.com/aws/aws-sdk-go-v2/config v1.27.24 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.24 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.27.2 // indirect + github.com/aws/aws-sdk-go-v2/config v1.27.18 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.18 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.22.1 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.2 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.30.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.20.11 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-logr/zapr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/goccy/go-json v0.10.3 // indirect github.com/gofrs/flock v0.8.1 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/cel-go v0.23.2 // indirect + github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.5 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/huandu/xstrings v1.5.0 // indirect - github.com/lestrrat-go/blackmagic v1.0.2 // 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/lestrrat-go/blackmagic v1.0.3 // indirect github.com/lestrrat-go/httpcc v1.0.1 // indirect github.com/lestrrat-go/httprc v1.0.6 // indirect github.com/lestrrat-go/iter v1.0.2 // indirect github.com/lestrrat-go/option v1.0.1 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // 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/oracle/oci-go-sdk/v65 v65.95.2 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.22.0 // indirect + github.com/prometheus/client_model v0.6.1 // indirect + github.com/prometheus/common v0.62.0 // indirect + github.com/prometheus/procfs v0.15.1 // indirect github.com/segmentio/asm v1.2.0 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/spf13/cast v1.7.0 // indirect + github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stoewer/go-strcase v1.3.0 // indirect + github.com/x448/float16 v0.8.4 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect - go.opentelemetry.io/otel v1.28.0 // indirect - go.opentelemetry.io/otel/metric v1.28.0 // indirect - go.opentelemetry.io/otel/trace v1.28.0 // indirect - golang.org/x/sync v0.10.0 // indirect + go.opentelemetry.io/auto/sdk v1.1.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect + go.opentelemetry.io/otel v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect + go.opentelemetry.io/otel/metric v1.33.0 // indirect + go.opentelemetry.io/otel/sdk v1.33.0 // indirect + go.opentelemetry.io/otel/trace v1.33.0 // indirect + go.opentelemetry.io/proto/otlp v1.4.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/net v0.38.0 // indirect + golang.org/x/oauth2 v0.27.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/term v0.30.0 // indirect + golang.org/x/text v0.23.0 // indirect + golang.org/x/time v0.9.0 // indirect + golang.org/x/tools v0.26.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/api v0.188.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240708141625-4ad9e859172b // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b // indirect - google.golang.org/grpc v1.65.0 // indirect -) - -require ( - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect - github.com/evanphx/json-patch/v5 v5.6.0 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-logr/logr v1.4.2 - github.com/go-logr/zapr v1.2.3 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.20.0 // indirect - github.com/go-openapi/swag v0.19.14 // indirect - github.com/go-resty/resty/v2 v2.13.1 - github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/gofuzz v1.1.0 // indirect - github.com/google/uuid v1.6.0 - github.com/imdario/mergo v0.3.12 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/mailru/easyjson v0.7.6 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.14.0 // indirect - github.com/prometheus/client_model v0.3.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - go.uber.org/atomic v1.7.0 // indirect - go.uber.org/multierr v1.6.0 // indirect - go.uber.org/zap v1.24.0 // indirect - golang.org/x/crypto v0.32.0 - golang.org/x/net v0.33.0 // indirect - golang.org/x/oauth2 v0.21.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/term v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect - golang.org/x/time v0.5.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect + google.golang.org/grpc v1.68.1 // indirect + google.golang.org/protobuf v1.36.5 // indirect + gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.26.1 - k8s.io/apiextensions-apiserver v0.26.1 // indirect - k8s.io/component-base v0.26.1 // indirect - k8s.io/klog/v2 v2.80.1 // indirect - k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect - k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + k8s.io/apiextensions-apiserver v0.33.0 // indirect + k8s.io/apiserver v0.33.0 // indirect + k8s.io/component-base v0.33.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/k8-operator/go.sum b/k8-operator/go.sum index a86541adc..3434d5971 100644 --- a/k8-operator/go.sum +++ b/k8-operator/go.sum @@ -1,222 +1,148 @@ +cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= +cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= 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.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/auth v0.7.0 h1:kf/x9B3WTbBUHkC+1VS8wwwli9TzhSt0vSTVBmMR8Ts= cloud.google.com/go/auth v0.7.0/go.mod h1:D+WqdrpcjmiCgWrXmLLxOVq1GACoE36chW6KXoEvuIw= cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4= cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= -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/compute/metadata v0.4.0 h1:vHzJCWaM4g8XIcm8kopr3XmDA4Gy/lblD3EhhSux05c= -cloud.google.com/go/compute/metadata v0.4.0/go.mod h1:SIQh1Kkb4ZJ8zJ874fqVkslA29PRXuleyj6vOzlbK7M= -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/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= +cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= cloud.google.com/go/iam v1.1.11 h1:0mQ8UKSfdHLut6pH9FM3bI55KWR46ketn0PuXleDyxw= cloud.google.com/go/iam v1.1.11/go.mod h1:biXoiLWYIKntto2joP+62sd9uW5EpkZmKIvfNcTWlnQ= -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= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= -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/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/aws/aws-sdk-go-v2 v1.30.1 h1:4y/5Dvfrhd1MxRDD77SrfsDaj8kUkkljU7XE83NPV+o= -github.com/aws/aws-sdk-go-v2 v1.30.1/go.mod h1:nIQjQVp5sfpQcTc9mPSr1B0PaWK5ByX9MOoDadSN4lc= -github.com/aws/aws-sdk-go-v2/config v1.27.24 h1:NM9XicZ5o1CBU/MZaHwFtimRpWx9ohAUAqkG6AqSqPo= -github.com/aws/aws-sdk-go-v2/config v1.27.24/go.mod h1:aXzi6QJTuQRVVusAO8/NxpdTeTyr/wRcybdDtfUwJSs= -github.com/aws/aws-sdk-go-v2/credentials v1.17.24 h1:YclAsrnb1/GTQNt2nzv+756Iw4mF8AOzcDfweWwwm/M= -github.com/aws/aws-sdk-go-v2/credentials v1.17.24/go.mod h1:Hld7tmnAkoBQdTMNYZGzztzKRdA4fCdn9L83LOoigac= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9 h1:Aznqksmd6Rfv2HQN9cpqIV/lQRMaIpJkLLaJ1ZI76no= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.9/go.mod h1:WQr3MY7AxGNxaqAtsDWn+fBxmd4XvLkzeqQ8P1VM0/w= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13 h1:5SAoZ4jYpGH4721ZNoS1znQrhOfZinOhc4XuTXx/nVc= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.13/go.mod h1:+rdA6ZLpaSeM7tSg/B0IEDinCIBJGmW8rKDFkYpP04g= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13 h1:WIijqeaAO7TYFLbhsZmi2rgLEAtWOC1LhxCAVTJlSKw= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.13/go.mod h1:i+kbfa76PQbWw/ULoWnp51EYVWH4ENln76fLQE3lXT8= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/aws/aws-sdk-go-v2 v1.27.2 h1:pLsTXqX93rimAOZG2FIYraDQstZaaGVVN4tNw65v0h8= +github.com/aws/aws-sdk-go-v2 v1.27.2/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM= +github.com/aws/aws-sdk-go-v2/config v1.27.18 h1:wFvAnwOKKe7QAyIxziwSKjmer9JBMH1vzIL6W+fYuKk= +github.com/aws/aws-sdk-go-v2/config v1.27.18/go.mod h1:0xz6cgdX55+kmppvPm2IaKzIXOheGJhAufacPJaXZ7c= +github.com/aws/aws-sdk-go-v2/credentials v1.17.18 h1:D/ALDWqK4JdY3OFgA2thcPO1c9aYTT5STS/CvnkqY1c= +github.com/aws/aws-sdk-go-v2/credentials v1.17.18/go.mod h1:JuitCWq+F5QGUrmMPsk945rop6bB57jdscu+Glozdnc= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5 h1:dDgptDO9dxeFkXy+tEgVkzSClHZje/6JkPW5aZyEvrQ= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5/go.mod h1:gjvE2KBUgUQhcv89jqxrIxH9GaKs1JbZzWejj/DaHGA= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9 h1:cy8ahBJuhtM8GTTSyOkfy6WVPV1IE+SS5/wfXUYuulw= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9/go.mod h1:CZBXGLaJnEZI6EVNcPd7a6B5IC5cA/GkRWtu9fp3S6Y= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9 h1:A4SYk07ef04+vxZToz9LWvAXl9LW0NClpPpMsi31cz0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9/go.mod h1:5jJcHuwDagxN+ErjQ3PU3ocf6Ylc/p9x+BLO/+X4iXw= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3 h1:dT3MqvGhSoaIhRseqw2I0yH81l7wiR2vjs57O51EAm8= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.3/go.mod h1:GlAeCkHwugxdHaueRr4nhPuY+WW+gR8UjlcqzPr1SPI= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15 h1:I9zMeF107l0rJrpnHpjEiiTSCKYAIw8mALiXcPsGBiA= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.15/go.mod h1:9xWJ3Q/S6Ojusz1UIkfycgD1mGirJfLLKqq3LPT7WN8= -github.com/aws/aws-sdk-go-v2/service/sso v1.22.1 h1:p1GahKIjyMDZtiKoIn0/jAj/TkMzfzndDv5+zi2Mhgc= -github.com/aws/aws-sdk-go-v2/service/sso v1.22.1/go.mod h1:/vWdhoIoYA5hYoPZ6fm7Sv4d8701PiG5VKe8/pPJL60= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.2 h1:ORnrOK0C4WmYV/uYt3koHEWBLYsRDwk2Np+eEoyV4Z0= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.2/go.mod h1:xyFHA4zGxgYkdD73VeezHt3vSKEG9EmFnGwoKlP00u4= -github.com/aws/aws-sdk-go-v2/service/sts v1.30.1 h1:+woJ607dllHJQtsnJLi52ycuqHMwlW+Wqm2Ppsfp4nQ= -github.com/aws/aws-sdk-go-v2/service/sts v1.30.1/go.mod h1:jiNR3JqT15Dm+QWq2SRgh0x0bCNSRP2L25+CqPNpJlQ= -github.com/aws/smithy-go v1.20.3 h1:ryHwveWzPV5BIof6fyDvor6V3iUL7nTfiTKXHiW05nE= -github.com/aws/smithy-go v1.20.3/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11 h1:o4T+fKxA3gTMcluBNZZXE9DNaMkJuUL1O3mffCUjoJo= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11/go.mod h1:84oZdJ+VjuJKs9v1UTC9NaodRZRseOXCTgku+vQJWR8= +github.com/aws/aws-sdk-go-v2/service/sso v1.20.11 h1:gEYM2GSpr4YNWc6hCd5nod4+d4kd9vWIAWrmGuLdlMw= +github.com/aws/aws-sdk-go-v2/service/sso v1.20.11/go.mod h1:gVvwPdPNYehHSP9Rs7q27U1EU+3Or2ZpXvzAYJNh63w= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5 h1:iXjh3uaH3vsVcnyZX7MqCoCfcyxIrVE9iOQruRaWPrQ= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5/go.mod h1:5ZXesEuy/QcO0WUnt+4sDkxhdXRHTu2yG0uCSH8B6os= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 h1:M/1u4HBpwLuMtjlxuI2y6HoVLzF5e2mfxHCg7ZVMYmk= +github.com/aws/aws-sdk-go-v2/service/sts v1.28.12/go.mod h1:kcfd+eTdEi/40FIbLq4Hif3XMXnl5b/+t/KTfLt9xIk= +github.com/aws/smithy-go v1.22.4 h1:uqXzVZNuNexwc/xrh6Tb56u89WDlJY6HS+KC0S4QSjw= +github.com/aws/smithy-go v1.22.4/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/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/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/cpuguy83/go-md2man/v2 v2.0.4/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/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/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/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= -github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= -github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= -github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= -github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= -github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g= -github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96ORAI6Q7d+0= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonreference v0.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/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM= +github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 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/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/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.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -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.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= +github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= 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.3/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.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.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/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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -224,68 +150,49 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -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/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA= github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E= -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/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= -github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/infisical/go-sdk v0.5.97 h1:veOi6Hduda6emtwjdUI5SBg2qd2iDQc5xLKqZ15KSoM= -github.com/infisical/go-sdk v0.5.97/go.mod h1:ExjqFLRz7LSpZpGluqDLvFl6dFBLq5LKyLW7GBaMAIs= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/infisical/go-sdk v0.5.99 h1:trvn7JhKYuSzDkc44h+yqToVjclkrRyP42t315k5kEE= github.com/infisical/go-sdk v0.5.99/go.mod h1:j2D2a5WPNdKXDfHO+3y/TNyLWh5Aq9QYS7EcGI96LZI= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 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/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 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/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -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/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 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/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k= -github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lestrrat-go/blackmagic v1.0.3 h1:94HXkVLxkZO9vJI/w2u1T0DAoprShFd13xtnSINtDWs= +github.com/lestrrat-go/blackmagic v1.0.3/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw= github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k= github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo= github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI= github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4= -github.com/lestrrat-go/jwx/v2 v2.1.4 h1:uBCMmJX8oRZStmKuMMOFb0Yh9xmEMgNJLgjuKKt4/qc= -github.com/lestrrat-go/jwx/v2 v2.1.4/go.mod h1:nWRbDFR1ALG2Z6GJbBXzfQaYyvn751KuuyySN2yR6is= +github.com/lestrrat-go/jwx/v2 v2.1.6 h1:hxM1gfDILk/l5ylers6BX/Eq1m/pnxe9NBwW6lVfecA= +github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU= github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2 h1:hAHbPm5IJGijwng3PWk09JkG9WeqChjprR5s9bBZ+OM= -github.com/matttproud/golang_protobuf_extensions v1.0.2/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= @@ -293,431 +200,210 @@ github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx 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 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/onsi/ginkgo/v2 v2.6.0 h1:9t9b9vRUbFq3C4qKFCGkVuq/fIHji802N1nrtkh1mNc= -github.com/onsi/ginkgo/v2 v2.6.0/go.mod h1:63DOGlLAH8+REH8jUGdL3YpCpu7JODesutUjdENfUAc= -github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E= -github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/oracle/oci-go-sdk/v65 v65.95.2 h1:0HJ0AgpLydp/DtvYrF2d4str2BjXOVAeNbuW7E07g94= github.com/oracle/oci-go-sdk/v65 v65.95.2/go.mod h1:u6XRPsw9tPziBh76K7GrrRXPa8P8W3BQeqJ6ZZt9VLA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= -github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= -github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= -github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU= github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= 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/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= +github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/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/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 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.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -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.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= -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= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -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.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= -go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -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-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0 h1:PS8wXpbyaDJQ2VDHHncMe9Vct0Zn1fEjpsjrLxGJoSc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.58.0/go.mod h1:HDBUsEjOuRC0EzKZ1bSaRGZWUBAzo+MhAcUUORSr4D0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= +go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= +go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-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-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= -golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= 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/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/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= 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/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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.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-20181114220301-adae6a3d119a/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-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= 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-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= -golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= 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/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/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-20190422165155-953cdadca894/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-20200106162015-b016eb3dc98e/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-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/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-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= 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.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.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -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.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= -golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-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-20191108193012-7d206e10da11/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-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= -gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= -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= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= google.golang.org/api v0.188.0 h1:51y8fJ/b1AaaBRJr4yWm96fPcuxSo0JcegXE3DaHQHw= google.golang.org/api v0.188.0/go.mod h1:VR0d+2SIiWOYG3r/jdm7adPW9hI2aRv9ETOSCQ9Beag= 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/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-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-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto/googleapis/api v0.0.0-20240708141625-4ad9e859172b h1:y/kpOWeX2pWERnbsvh/hF+Zmo69wVmjyZhstreXQQeA= -google.golang.org/genproto/googleapis/api v0.0.0-20240708141625-4ad9e859172b/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b h1:04+jVzTs2XBnOZcPsLnmrTGqltqJbZQ1Ey26hjYdQQ0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= 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.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= -google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= 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= @@ -726,67 +412,51 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi 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.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 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-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +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/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/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.0-20210107192922-496545a6307b/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.26.1 h1:f+SWYiPd/GsiWwVRz+NbFyCgvv75Pk9NK6dlkZgpCRQ= -k8s.io/api v0.26.1/go.mod h1:xd/GBNgR0f707+ATNyPmQ1oyKSgndzXij81FzWGsejg= -k8s.io/apiextensions-apiserver v0.26.1 h1:cB8h1SRk6e/+i3NOrQgSFij1B2S0Y0wDoNl66bn8RMI= -k8s.io/apiextensions-apiserver v0.26.1/go.mod h1:AptjOSXDGuE0JICx/Em15PaoO7buLwTs0dGleIHixSM= -k8s.io/apimachinery v0.26.1 h1:8EZ/eGJL+hY/MYCNwhmDzVqq2lPl3N3Bo8rvweJwXUQ= -k8s.io/apimachinery v0.26.1/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74= -k8s.io/client-go v0.26.1 h1:87CXzYJnAMGaa/IDDfRdhTzxk/wzGZ+/HUQpqgVSZXU= -k8s.io/client-go v0.26.1/go.mod h1:IWNSglg+rQ3OcvDkhY6+QLeasV4OYHDjdqeWkDQZwGE= -k8s.io/component-base v0.26.1 h1:4ahudpeQXHZL5kko+iDHqLj/FSGAEUnSVO0EBbgDd+4= -k8s.io/component-base v0.26.1/go.mod h1:VHrLR0b58oC035w6YQiBSbtsf0ThuSwXP+p5dD/kAWU= -k8s.io/klog/v2 v2.80.1 h1:atnLQ121W371wYYFawwYx1aEY2eUfs4l3J72wtgAwV4= -k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= -k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280/go.mod h1:+Axhij7bCpeqhklhUTe3xmOn6bWxolyZEeyaFpjGtl4= -k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 h1:KTgPnR10d5zhztWptI952TNtt/4u5h3IzDXkdIMuo2Y= -k8s.io/utils v0.0.0-20221128185143-99ec85e7a448/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.14.4 h1:Kd/Qgx5pd2XUL08eOV2vwIq3L9GhIbJ5Nxengbd4/0M= -sigs.k8s.io/controller-runtime v0.14.4/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/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= -software.sslmate.com/src/go-pkcs12 v0.5.0 h1:EC6R394xgENTpZ4RltKydeDUjtlM5drOYIG9c6TVj2M= -software.sslmate.com/src/go-pkcs12 v0.5.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= +k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU= +k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM= +k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs= +k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc= +k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ= +k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/apiserver v0.33.0 h1:QqcM6c+qEEjkOODHppFXRiw/cE2zP85704YrQ9YaBbc= +k8s.io/apiserver v0.33.0/go.mod h1:EixYOit0YTxt8zrO2kBU7ixAtxFce9gKGq367nFmqI8= +k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98= +k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg= +k8s.io/component-base v0.33.0 h1:Ot4PyJI+0JAD9covDhwLp9UNkUja209OzsJ4FzScBNk= +k8s.io/component-base v0.33.0/go.mod h1:aXYZLbw3kihdkOPMDhWbjGCO6sg+luw554KP51t8qCU= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= +sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +software.sslmate.com/src/go-pkcs12 v0.6.0 h1:f3sQittAeF+pao32Vb+mkli+ZyT+VwKaD014qFGq6oU= +software.sslmate.com/src/go-pkcs12 v0.6.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI= diff --git a/k8-operator/hack/boilerplate.go.txt b/k8-operator/hack/boilerplate.go.txt index 29c55ecda..221dcbe0b 100644 --- a/k8-operator/hack/boilerplate.go.txt +++ b/k8-operator/hack/boilerplate.go.txt @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2025. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/k8-operator/k8-operator/internal/api/api.go b/k8-operator/internal/api/api.go similarity index 100% rename from k8-operator/k8-operator/internal/api/api.go rename to k8-operator/internal/api/api.go diff --git a/k8-operator/k8-operator/internal/api/models.go b/k8-operator/internal/api/models.go similarity index 100% rename from k8-operator/k8-operator/internal/api/models.go rename to k8-operator/internal/api/models.go diff --git a/k8-operator/k8-operator/internal/api/variables.go b/k8-operator/internal/api/variables.go similarity index 100% rename from k8-operator/k8-operator/internal/api/variables.go rename to k8-operator/internal/api/variables.go diff --git a/k8-operator/k8-operator/internal/constants/constants.go b/k8-operator/internal/constants/constants.go similarity index 100% rename from k8-operator/k8-operator/internal/constants/constants.go rename to k8-operator/internal/constants/constants.go diff --git a/k8-operator/internal/controller/infisicaldynamicsecret_controller.go b/k8-operator/internal/controller/infisicaldynamicsecret_controller.go index 8d75eef69..adbc6b03c 100644 --- a/k8-operator/internal/controller/infisicaldynamicsecret_controller.go +++ b/k8-operator/internal/controller/infisicaldynamicsecret_controller.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2025. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,46 +18,219 @@ package controller import ( "context" + "fmt" + "math/rand" + "time" + infisicaldynamicsecret "github.com/Infisical/infisical/k8-operator/internal/services/infisicaldynamicsecret" + "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/event" + "sigs.k8s.io/controller-runtime/pkg/predicate" secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + "github.com/Infisical/infisical/k8-operator/internal/constants" + "github.com/Infisical/infisical/k8-operator/internal/controllerhelpers" + "github.com/Infisical/infisical/k8-operator/internal/util" + "github.com/go-logr/logr" ) // InfisicalDynamicSecretReconciler reconciles a InfisicalDynamicSecret object type InfisicalDynamicSecretReconciler struct { client.Client - Scheme *runtime.Scheme + BaseLogger logr.Logger + Scheme *runtime.Scheme + Random *rand.Rand +} + +var infisicalDynamicSecretsResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables) + +func (r *InfisicalDynamicSecretReconciler) GetLogger(req ctrl.Request) logr.Logger { + return r.BaseLogger.WithValues("infisicaldynamicsecret", req.NamespacedName) } // +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/status,verbs=get;update;patch // +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/finalizers,verbs=update +// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;delete +// +kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;delete +// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=list;watch;get;update +// +kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch +//+kubebuilder:rbac:groups="",resources=pods,verbs=get;list +//+kubebuilder:rbac:groups="authentication.k8s.io",resources=tokenreviews,verbs=create +//+kubebuilder:rbac:groups="",resources=serviceaccounts/token,verbs=create -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the InfisicalDynamicSecret object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.1/pkg/reconcile func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - _ = log.FromContext(ctx) - // TODO(user): your logic here + logger := r.GetLogger(req) - return ctrl.Result{}, nil + var infisicalDynamicSecretCRD secretsv1alpha1.InfisicalDynamicSecret + requeueTime := time.Second * 5 + + err := r.Get(ctx, req.NamespacedName, &infisicalDynamicSecretCRD) + if err != nil { + if errors.IsNotFound(err) { + logger.Info("Infisical Dynamic Secret CRD not found") + return ctrl.Result{ + Requeue: false, + }, nil + } else { + logger.Error(err, "Unable to fetch Infisical Dynamic Secret CRD from cluster") + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil + } + } + + // Add finalizer if it doesn't exist + if !controllerutil.ContainsFinalizer(&infisicalDynamicSecretCRD, constants.INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME) { + controllerutil.AddFinalizer(&infisicalDynamicSecretCRD, constants.INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME) + if err := r.Update(ctx, &infisicalDynamicSecretCRD); err != nil { + return ctrl.Result{}, err + } + } + + // Check if it's being deleted + if !infisicalDynamicSecretCRD.DeletionTimestamp.IsZero() { + logger.Info("Handling deletion of InfisicalDynamicSecret") + if controllerutil.ContainsFinalizer(&infisicalDynamicSecretCRD, constants.INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME) { + // We remove finalizers before running deletion logic to be completely safe from stuck resources + infisicalDynamicSecretCRD.ObjectMeta.Finalizers = []string{} + if err := r.Update(ctx, &infisicalDynamicSecretCRD); err != nil { + logger.Error(err, fmt.Sprintf("Error removing finalizers from InfisicalDynamicSecret %s", infisicalDynamicSecretCRD.Name)) + return ctrl.Result{}, err + } + + // Initialize the business logic handler + handler := infisicaldynamicsecret.NewInfisicalDynamicSecretHandler(r.Client, r.Scheme) + + err := handler.HandleLeaseRevocation(ctx, logger, &infisicalDynamicSecretCRD, infisicalDynamicSecretsResourceVariablesMap) + + if infisicalDynamicSecretsResourceVariablesMap != nil { + if rv, ok := infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecretCRD.GetUID())]; ok { + rv.CancelCtx() + delete(infisicalDynamicSecretsResourceVariablesMap, string(infisicalDynamicSecretCRD.GetUID())) + } + } + + if err != nil { + return ctrl.Result{}, err // Even if this fails, we still want to delete the CRD + } + + } + return ctrl.Result{}, nil + } + + // Get modified/default config + infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client) + if err != nil { + logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime)) + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil + } + + // Initialize the business logic handler + handler := infisicaldynamicsecret.NewInfisicalDynamicSecretHandler(r.Client, r.Scheme) + + // Setup API configuration through business logic + err = handler.SetupAPIConfig(infisicalDynamicSecretCRD, infisicalConfig) + if err != nil { + logger.Error(err, fmt.Sprintf("unable to setup API configuration. Will requeue after [requeueTime=%v]", requeueTime)) + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil + } + + // Handle CA certificate through business logic + err = handler.HandleCACertificate(ctx, infisicalDynamicSecretCRD) + if err != nil { + logger.Error(err, fmt.Sprintf("unable to handle CA certificate. Will requeue after [requeueTime=%v]", requeueTime)) + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil + } + + nextReconcile, err := handler.ReconcileInfisicalDynamicSecret(ctx, logger, &infisicalDynamicSecretCRD, infisicalDynamicSecretsResourceVariablesMap) + handler.SetReconcileConditionStatus(ctx, logger, &infisicalDynamicSecretCRD, err) + + if err == nil && nextReconcile.Seconds() >= 5 { + requeueTime = nextReconcile + } + + if err != nil { + logger.Error(err, fmt.Sprintf("unable to reconcile Infisical Dynamic Secret. Will requeue after [requeueTime=%v]", requeueTime)) + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil + } + + numDeployments, err := controllerhelpers.ReconcileDeploymentsWithManagedSecrets(ctx, r.Client, logger, infisicalDynamicSecretCRD.Spec.ManagedSecretReference) + handler.SetReconcileAutoRedeploymentConditionStatus(ctx, logger, &infisicalDynamicSecretCRD, numDeployments, err) + + if err != nil { + logger.Error(err, fmt.Sprintf("unable to reconcile auto redeployment. Will requeue after [requeueTime=%v]", requeueTime)) + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil + } + + // Sync again after the specified time + logger.Info(fmt.Sprintf("Next reconciliation in [requeueTime=%v]", requeueTime)) + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil } -// SetupWithManager sets up the controller with the Manager. func (r *InfisicalDynamicSecretReconciler) SetupWithManager(mgr ctrl.Manager) error { + + // Custom predicate that allows both spec changes and deletions + specChangeOrDelete := predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { + // Only reconcile if spec/generation changed + + isSpecOrGenerationChange := e.ObjectOld.GetGeneration() != e.ObjectNew.GetGeneration() + + if isSpecOrGenerationChange { + if infisicalDynamicSecretsResourceVariablesMap != nil { + if rv, ok := infisicalDynamicSecretsResourceVariablesMap[string(e.ObjectNew.GetUID())]; ok { + rv.CancelCtx() + delete(infisicalDynamicSecretsResourceVariablesMap, string(e.ObjectNew.GetUID())) + } + } + } + + return isSpecOrGenerationChange + }, + DeleteFunc: func(e event.DeleteEvent) bool { + // Always reconcile on deletion + + if infisicalDynamicSecretsResourceVariablesMap != nil { + if rv, ok := infisicalDynamicSecretsResourceVariablesMap[string(e.Object.GetUID())]; ok { + rv.CancelCtx() + delete(infisicalDynamicSecretsResourceVariablesMap, string(e.Object.GetUID())) + } + } + + return true + }, + CreateFunc: func(e event.CreateEvent) bool { + // Reconcile on creation + return true + }, + GenericFunc: func(e event.GenericEvent) bool { + // Ignore generic events + return false + }, + } + return ctrl.NewControllerManagedBy(mgr). - For(&secretsv1alpha1.InfisicalDynamicSecret{}). - Named("infisicaldynamicsecret"). + For(&secretsv1alpha1.InfisicalDynamicSecret{}, builder.WithPredicates( + specChangeOrDelete, + )). Complete(r) } diff --git a/k8-operator/k8-operator/internal/controller/infisicaldynamicsecret_controller_test.go b/k8-operator/internal/controller/infisicaldynamicsecret_controller_test.go similarity index 100% rename from k8-operator/k8-operator/internal/controller/infisicaldynamicsecret_controller_test.go rename to k8-operator/internal/controller/infisicaldynamicsecret_controller_test.go diff --git a/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_controller.go b/k8-operator/internal/controller/infisicalpushsecret_controller.go similarity index 76% rename from k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_controller.go rename to k8-operator/internal/controller/infisicalpushsecret_controller.go index 3794c2f17..dc5bfad8b 100644 --- a/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_controller.go +++ b/k8-operator/internal/controller/infisicalpushsecret_controller.go @@ -1,10 +1,27 @@ -package controllers +/* +Copyright 2025. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller import ( "context" "fmt" "time" + infisicalpushsecret "github.com/Infisical/infisical/k8-operator/internal/services/infisicalpushsecret" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -17,17 +34,15 @@ import ( "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" - "github.com/Infisical/infisical/k8-operator/packages/api" - "github.com/Infisical/infisical/k8-operator/packages/constants" - controllerhelpers "github.com/Infisical/infisical/k8-operator/packages/controllerhelpers" - "github.com/Infisical/infisical/k8-operator/packages/util" + "github.com/Infisical/infisical/k8-operator/internal/constants" + "github.com/Infisical/infisical/k8-operator/internal/controllerhelpers" + "github.com/Infisical/infisical/k8-operator/internal/util" "github.com/go-logr/logr" ) -// InfisicalSecretReconciler reconciles a InfisicalSecret object +// InfisicalPushSecretReconciler reconciles a InfisicalPushSecretSecret object type InfisicalPushSecretReconciler struct { client.Client IsNamespaceScoped bool @@ -52,10 +67,6 @@ func (r *InfisicalPushSecretReconciler) GetLogger(req ctrl.Request) logr.Logger //+kubebuilder:rbac:groups="authentication.k8s.io",resources=tokenreviews,verbs=create //+kubebuilder:rbac:groups="",resources=serviceaccounts/token,verbs=create //+kubebuilder:rbac:groups=secrets.infisical.com,resources=clustergenerators,verbs=get;list;watch;create;update;patch;delete -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.13.1/pkg/reconcile func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { @@ -68,7 +79,9 @@ func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl. if err != nil { if errors.IsNotFound(err) { logger.Info("Infisical Push Secret CRD not found") - r.DeleteManagedSecrets(ctx, logger, infisicalPushSecretCRD) + // Initialize the business logic handler + handler := infisicalpushsecret.NewInfisicalPushSecretHandler(r.Client, r.Scheme, r.IsNamespaceScoped) + handler.DeleteManagedSecrets(ctx, logger, &infisicalPushSecretCRD, infisicalPushSecretResourceVariablesMap) return ctrl.Result{ Requeue: false, @@ -100,7 +113,10 @@ func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl. return ctrl.Result{}, err } - if err := r.DeleteManagedSecrets(ctx, logger, infisicalPushSecretCRD); err != nil { + // Initialize the business logic handler + handler := infisicalpushsecret.NewInfisicalPushSecretHandler(r.Client, r.Scheme, r.IsNamespaceScoped) + + if err := handler.DeleteManagedSecrets(ctx, logger, &infisicalPushSecretCRD, infisicalPushSecretResourceVariablesMap); err != nil { return ctrl.Result{}, err // Even if this fails, we still want to delete the CRD } @@ -155,33 +171,39 @@ func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl. } } - if infisicalPushSecretCRD.Spec.HostAPI == "" { - api.API_HOST_URL = infisicalConfig["hostAPI"] - } else { - api.API_HOST_URL = util.AppendAPIEndpoint(infisicalPushSecretCRD.Spec.HostAPI) - } + // Initialize the business logic handler + handler := infisicalpushsecret.NewInfisicalPushSecretHandler(r.Client, r.Scheme, r.IsNamespaceScoped) - if infisicalPushSecretCRD.Spec.TLS.CaRef.SecretName != "" { - api.API_CA_CERTIFICATE, err = r.getInfisicalCaCertificateFromKubeSecret(ctx, infisicalPushSecretCRD) - if err != nil { - if requeueTime != 0 { - logger.Error(err, fmt.Sprintf("unable to fetch CA certificate. Will requeue after [requeueTime=%v]", requeueTime)) - return ctrl.Result{ - RequeueAfter: requeueTime, - }, nil - } else { - logger.Error(err, "unable to fetch CA certificate") - return ctrl.Result{}, err - } + // Setup API configuration through business logic + err = handler.SetupAPIConfig(infisicalPushSecretCRD, infisicalConfig) + if err != nil { + if requeueTime != 0 { + logger.Error(err, fmt.Sprintf("unable to setup API configuration. Will requeue after [requeueTime=%v]", requeueTime)) + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil + } else { + logger.Error(err, "unable to setup API configuration") + return ctrl.Result{}, err } - - logger.Info("Using custom CA certificate...") - } else { - api.API_CA_CERTIFICATE = "" } - err = r.ReconcileInfisicalPushSecret(ctx, logger, infisicalPushSecretCRD) - r.SetReconcileStatusCondition(ctx, &infisicalPushSecretCRD, err) + // Handle CA certificate through business logic + err = handler.HandleCACertificate(ctx, infisicalPushSecretCRD) + if err != nil { + if requeueTime != 0 { + logger.Error(err, fmt.Sprintf("unable to fetch CA certificate. Will requeue after [requeueTime=%v]", requeueTime)) + return ctrl.Result{ + RequeueAfter: requeueTime, + }, nil + } else { + logger.Error(err, "unable to fetch CA certificate") + return ctrl.Result{}, err + } + } + + err = handler.ReconcileInfisicalPushSecret(ctx, logger, &infisicalPushSecretCRD, infisicalPushSecretResourceVariablesMap) + handler.SetReconcileStatusCondition(ctx, &infisicalPushSecretCRD, err) if err != nil { if requeueTime != 0 { @@ -254,14 +276,14 @@ func (r *InfisicalPushSecretReconciler) SetupWithManager(mgr ctrl.Manager) error specChangeOrDelete, )). Watches( - &source.Kind{Type: &corev1.Secret{}}, + &corev1.Secret{}, handler.EnqueueRequestsFromMapFunc(r.findPushSecretsForSecret), ) if !r.IsNamespaceScoped { r.BaseLogger.Info("Watching ClusterGenerators for non-namespace scoped operator") controllerManager.Watches( - &source.Kind{Type: &secretsv1alpha1.ClusterGenerator{}}, + &secretsv1alpha1.ClusterGenerator{}, handler.EnqueueRequestsFromMapFunc(r.findPushSecretsForClusterGenerator), ) } else { @@ -271,8 +293,7 @@ func (r *InfisicalPushSecretReconciler) SetupWithManager(mgr ctrl.Manager) error return controllerManager.Complete(r) } -func (r *InfisicalPushSecretReconciler) findPushSecretsForClusterGenerator(o client.Object) []reconcile.Request { - ctx := context.Background() +func (r *InfisicalPushSecretReconciler) findPushSecretsForClusterGenerator(ctx context.Context, o client.Object) []reconcile.Request { pushSecrets := &secretsv1alpha1.InfisicalPushSecretList{} if err := r.List(ctx, pushSecrets); err != nil { return []reconcile.Request{} @@ -303,8 +324,7 @@ func (r *InfisicalPushSecretReconciler) findPushSecretsForClusterGenerator(o cli return requests } -func (r *InfisicalPushSecretReconciler) findPushSecretsForSecret(o client.Object) []reconcile.Request { - ctx := context.Background() +func (r *InfisicalPushSecretReconciler) findPushSecretsForSecret(ctx context.Context, o client.Object) []reconcile.Request { pushSecrets := &secretsv1alpha1.InfisicalPushSecretList{} if err := r.List(ctx, pushSecrets); err != nil { return []reconcile.Request{} diff --git a/k8-operator/k8-operator/internal/controller/infisicalpushsecretsecret_controller_test.go b/k8-operator/internal/controller/infisicalpushsecret_controller_test.go similarity index 92% rename from k8-operator/k8-operator/internal/controller/infisicalpushsecretsecret_controller_test.go rename to k8-operator/internal/controller/infisicalpushsecret_controller_test.go index aca7994d8..160ef7e43 100644 --- a/k8-operator/k8-operator/internal/controller/infisicalpushsecretsecret_controller_test.go +++ b/k8-operator/internal/controller/infisicalpushsecret_controller_test.go @@ -40,13 +40,13 @@ var _ = Describe("InfisicalPushSecretSecret Controller", func() { Name: resourceName, Namespace: "default", // TODO(user):Modify as needed } - infisicalpushsecretsecret := &secretsv1alpha1.InfisicalPushSecretSecret{} + infisicalpushsecretsecret := &secretsv1alpha1.InfisicalPushSecret{} BeforeEach(func() { By("creating the custom resource for the Kind InfisicalPushSecretSecret") err := k8sClient.Get(ctx, typeNamespacedName, infisicalpushsecretsecret) if err != nil && errors.IsNotFound(err) { - resource := &secretsv1alpha1.InfisicalPushSecretSecret{ + resource := &secretsv1alpha1.InfisicalPushSecret{ ObjectMeta: metav1.ObjectMeta{ Name: resourceName, Namespace: "default", @@ -59,7 +59,7 @@ var _ = Describe("InfisicalPushSecretSecret Controller", func() { AfterEach(func() { // TODO(user): Cleanup logic after each test, like removing the resource instance. - resource := &secretsv1alpha1.InfisicalPushSecretSecret{} + resource := &secretsv1alpha1.InfisicalPushSecret{} err := k8sClient.Get(ctx, typeNamespacedName, resource) Expect(err).NotTo(HaveOccurred()) @@ -68,7 +68,7 @@ var _ = Describe("InfisicalPushSecretSecret Controller", func() { }) It("should successfully reconcile the resource", func() { By("Reconciling the created resource") - controllerReconciler := &InfisicalPushSecretSecretReconciler{ + controllerReconciler := &InfisicalPushSecretReconciler{ Client: k8sClient, Scheme: k8sClient.Scheme(), } diff --git a/k8-operator/k8-operator/internal/controller/infisicalsecret_controller.go b/k8-operator/internal/controller/infisicalsecret_controller.go similarity index 93% rename from k8-operator/k8-operator/internal/controller/infisicalsecret_controller.go rename to k8-operator/internal/controller/infisicalsecret_controller.go index 13d98243d..3cea12842 100644 --- a/k8-operator/k8-operator/internal/controller/infisicalsecret_controller.go +++ b/k8-operator/internal/controller/infisicalsecret_controller.go @@ -147,10 +147,10 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ } // Initialize the business logic handler - businessLogic := infisicalsecret.NewInfisicalSecretHandler(r.Client, r.Scheme) + handler := infisicalsecret.NewInfisicalSecretHandler(r.Client, r.Scheme) // Setup API configuration through business logic - err = businessLogic.SetupAPIConfig(infisicalSecretCRD, infisicalConfig) + err = handler.SetupAPIConfig(infisicalSecretCRD, infisicalConfig) if err != nil { logger.Error(err, fmt.Sprintf("unable to setup API configuration. Will requeue after [requeueTime=%v]", requeueTime)) return ctrl.Result{ @@ -159,7 +159,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ } // Handle CA certificate through business logic - err = businessLogic.HandleCACertificate(ctx, infisicalSecretCRD) + err = handler.HandleCACertificate(ctx, infisicalSecretCRD) if err != nil { logger.Error(err, fmt.Sprintf("unable to handle CA certificate. Will requeue after [requeueTime=%v]", requeueTime)) return ctrl.Result{ @@ -167,8 +167,8 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ }, nil } - secretsCount, err := businessLogic.ReconcileInfisicalSecret(ctx, logger, &infisicalSecretCRD, managedKubeSecretReferences, managedKubeConfigMapReferences, infisicalSecretResourceVariablesMap) - businessLogic.SetReadyToSyncSecretsConditions(ctx, logger, &infisicalSecretCRD, secretsCount, err) + secretsCount, err := handler.ReconcileInfisicalSecret(ctx, logger, &infisicalSecretCRD, managedKubeSecretReferences, managedKubeConfigMapReferences, infisicalSecretResourceVariablesMap) + handler.SetReadyToSyncSecretsConditions(ctx, logger, &infisicalSecretCRD, secretsCount, err) if err != nil { logger.Error(err, fmt.Sprintf("unable to reconcile InfisicalSecret. Will requeue after [requeueTime=%v]", requeueTime)) @@ -178,7 +178,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ } numDeployments, err := controllerhelpers.ReconcileDeploymentsWithMultipleManagedSecrets(ctx, r.Client, logger, managedKubeSecretReferences) - businessLogic.SetInfisicalAutoRedeploymentReady(ctx, logger, &infisicalSecretCRD, numDeployments, err) + handler.SetInfisicalAutoRedeploymentReady(ctx, logger, &infisicalSecretCRD, numDeployments, err) if err != nil { logger.Error(err, fmt.Sprintf("unable to reconcile auto redeployment. Will requeue after [requeueTime=%v]", requeueTime)) diff --git a/k8-operator/k8-operator/internal/controller/infisicalsecret_controller_test.go b/k8-operator/internal/controller/infisicalsecret_controller_test.go similarity index 100% rename from k8-operator/k8-operator/internal/controller/infisicalsecret_controller_test.go rename to k8-operator/internal/controller/infisicalsecret_controller_test.go diff --git a/k8-operator/k8-operator/internal/controller/suite_test.go b/k8-operator/internal/controller/suite_test.go similarity index 100% rename from k8-operator/k8-operator/internal/controller/suite_test.go rename to k8-operator/internal/controller/suite_test.go diff --git a/k8-operator/k8-operator/internal/controllerhelpers/controllerhelpers.go b/k8-operator/internal/controllerhelpers/controllerhelpers.go similarity index 100% rename from k8-operator/k8-operator/internal/controllerhelpers/controllerhelpers.go rename to k8-operator/internal/controllerhelpers/controllerhelpers.go diff --git a/k8-operator/k8-operator/internal/controllerutil/util.go b/k8-operator/internal/controllerutil/util.go similarity index 100% rename from k8-operator/k8-operator/internal/controllerutil/util.go rename to k8-operator/internal/controllerutil/util.go diff --git a/k8-operator/k8-operator/internal/crypto/crypto.go b/k8-operator/internal/crypto/crypto.go similarity index 100% rename from k8-operator/k8-operator/internal/crypto/crypto.go rename to k8-operator/internal/crypto/crypto.go diff --git a/k8-operator/k8-operator/internal/generator/generator.go b/k8-operator/internal/generator/generator.go similarity index 100% rename from k8-operator/k8-operator/internal/generator/generator.go rename to k8-operator/internal/generator/generator.go diff --git a/k8-operator/k8-operator/internal/generator/password.go b/k8-operator/internal/generator/password.go similarity index 100% rename from k8-operator/k8-operator/internal/generator/password.go rename to k8-operator/internal/generator/password.go diff --git a/k8-operator/k8-operator/internal/generator/uuid.go b/k8-operator/internal/generator/uuid.go similarity index 100% rename from k8-operator/k8-operator/internal/generator/uuid.go rename to k8-operator/internal/generator/uuid.go diff --git a/k8-operator/packages/model/model.go b/k8-operator/internal/model/model.go similarity index 100% rename from k8-operator/packages/model/model.go rename to k8-operator/internal/model/model.go diff --git a/k8-operator/controllers/infisicaldynamicsecret/conditions.go b/k8-operator/internal/services/infisicaldynamicsecret/conditions.go similarity index 99% rename from k8-operator/controllers/infisicaldynamicsecret/conditions.go rename to k8-operator/internal/services/infisicaldynamicsecret/conditions.go index a26e5b71d..0a2235442 100644 --- a/k8-operator/controllers/infisicaldynamicsecret/conditions.go +++ b/k8-operator/internal/services/infisicaldynamicsecret/conditions.go @@ -1,4 +1,4 @@ -package controllers +package infisicaldynamicsecret import ( "context" diff --git a/k8-operator/internal/services/infisicaldynamicsecret/handler.go b/k8-operator/internal/services/infisicaldynamicsecret/handler.go new file mode 100644 index 000000000..71ce591d5 --- /dev/null +++ b/k8-operator/internal/services/infisicaldynamicsecret/handler.go @@ -0,0 +1,110 @@ +package infisicaldynamicsecret + +import ( + "context" + "fmt" + "math/rand" + "time" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + "github.com/Infisical/infisical/k8-operator/internal/api" + "github.com/Infisical/infisical/k8-operator/internal/util" + "github.com/go-logr/logr" + k8Errors "k8s.io/apimachinery/pkg/api/errors" +) + +type InfisicalDynamicSecretHandler struct { + client.Client + Scheme *runtime.Scheme + Random *rand.Rand +} + +func NewInfisicalDynamicSecretHandler(client client.Client, scheme *runtime.Scheme) *InfisicalDynamicSecretHandler { + return &InfisicalDynamicSecretHandler{ + Client: client, + Scheme: scheme, + Random: rand.New(rand.NewSource(time.Now().UnixNano())), + } +} + +func (h *InfisicalDynamicSecretHandler) SetupAPIConfig(infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, infisicalConfig map[string]string) error { + if infisicalDynamicSecret.Spec.HostAPI == "" { + api.API_HOST_URL = infisicalConfig["hostAPI"] + } else { + api.API_HOST_URL = util.AppendAPIEndpoint(infisicalDynamicSecret.Spec.HostAPI) + } + return nil +} + +func (h *InfisicalDynamicSecretHandler) getInfisicalCaCertificateFromKubeSecret(ctx context.Context, infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret) (caCertificate string, err error) { + + caCertificateFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, h.Client, types.NamespacedName{ + Namespace: infisicalDynamicSecret.Spec.TLS.CaRef.SecretNamespace, + Name: infisicalDynamicSecret.Spec.TLS.CaRef.SecretName, + }) + + if k8Errors.IsNotFound(err) { + return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err) + } + + if err != nil { + return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err) + } + + caCertificateFromSecret := string(caCertificateFromKubeSecret.Data[infisicalDynamicSecret.Spec.TLS.CaRef.SecretKey]) + + return caCertificateFromSecret, nil +} + +func (h *InfisicalDynamicSecretHandler) HandleCACertificate(ctx context.Context, infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret) error { + if infisicalDynamicSecret.Spec.TLS.CaRef.SecretName != "" { + caCert, err := h.getInfisicalCaCertificateFromKubeSecret(ctx, infisicalDynamicSecret) + if err != nil { + return err + } + api.API_CA_CERTIFICATE = caCert + } else { + api.API_CA_CERTIFICATE = "" + } + return nil +} + +func (h *InfisicalDynamicSecretHandler) ReconcileInfisicalDynamicSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) (time.Duration, error) { + reconciler := &InfisicalDynamicSecretReconciler{ + Client: h.Client, + Scheme: h.Scheme, + Random: h.Random, + } + return reconciler.ReconcileInfisicalDynamicSecret(ctx, logger, infisicalDynamicSecret, resourceVariablesMap) +} + +func (h *InfisicalDynamicSecretHandler) HandleLeaseRevocation(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) error { + reconciler := &InfisicalDynamicSecretReconciler{ + Client: h.Client, + Scheme: h.Scheme, + Random: h.Random, + } + return reconciler.HandleLeaseRevocation(ctx, logger, infisicalDynamicSecret, resourceVariablesMap) +} + +func (h *InfisicalDynamicSecretHandler) SetReconcileConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, errorToConditionOn error) { + reconciler := &InfisicalDynamicSecretReconciler{ + Client: h.Client, + Scheme: h.Scheme, + Random: h.Random, + } + reconciler.SetReconcileConditionStatus(ctx, logger, infisicalDynamicSecret, errorToConditionOn) +} + +func (h *InfisicalDynamicSecretHandler) SetReconcileAutoRedeploymentConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, numDeployments int, errorToConditionOn error) { + reconciler := &InfisicalDynamicSecretReconciler{ + Client: h.Client, + Scheme: h.Scheme, + Random: h.Random, + } + reconciler.SetReconcileAutoRedeploymentConditionStatus(ctx, logger, infisicalDynamicSecret, numDeployments, errorToConditionOn) +} diff --git a/k8-operator/controllers/infisicaldynamicsecret/infisicaldynamicsecret_helper.go b/k8-operator/internal/services/infisicaldynamicsecret/reconciler.go similarity index 87% rename from k8-operator/controllers/infisicaldynamicsecret/infisicaldynamicsecret_helper.go rename to k8-operator/internal/services/infisicaldynamicsecret/reconciler.go index daae2bf9d..6525dc433 100644 --- a/k8-operator/controllers/infisicaldynamicsecret/infisicaldynamicsecret_helper.go +++ b/k8-operator/internal/services/infisicaldynamicsecret/reconciler.go @@ -1,16 +1,17 @@ -package controllers +package infisicaldynamicsecret import ( "context" "errors" "fmt" + "math/rand" "strings" "time" "github.com/Infisical/infisical/k8-operator/api/v1alpha1" - "github.com/Infisical/infisical/k8-operator/packages/api" - "github.com/Infisical/infisical/k8-operator/packages/constants" - "github.com/Infisical/infisical/k8-operator/packages/util" + "github.com/Infisical/infisical/k8-operator/internal/api" + "github.com/Infisical/infisical/k8-operator/internal/constants" + "github.com/Infisical/infisical/k8-operator/internal/util" "github.com/go-logr/logr" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" @@ -20,9 +21,16 @@ import ( infisicalSdk "github.com/infisical/go-sdk" k8Errors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" ) +type InfisicalDynamicSecretReconciler struct { + client.Client + Scheme *runtime.Scheme + Random *rand.Rand +} + func (r *InfisicalDynamicSecretReconciler) createInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, versionAnnotationValue string) error { secretType := infisicalDynamicSecret.Spec.ManagedSecretReference.SecretType @@ -107,31 +115,11 @@ func (r *InfisicalDynamicSecretReconciler) handleAuthentication(ctx context.Cont } -func (r *InfisicalDynamicSecretReconciler) getInfisicalCaCertificateFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalDynamicSecret) (caCertificate string, err error) { - - caCertificateFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{ - Namespace: infisicalSecret.Spec.TLS.CaRef.SecretNamespace, - Name: infisicalSecret.Spec.TLS.CaRef.SecretName, - }) - - if k8Errors.IsNotFound(err) { - return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err) - } - - if err != nil { - return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err) - } - - caCertificateFromSecret := string(caCertificateFromKubeSecret.Data[infisicalSecret.Spec.TLS.CaRef.SecretKey]) - - return caCertificateFromSecret, nil -} - -func (r *InfisicalDynamicSecretReconciler) getResourceVariables(infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret) util.ResourceVariables { +func (r *InfisicalDynamicSecretReconciler) getResourceVariables(infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) util.ResourceVariables { var resourceVariables util.ResourceVariables - if _, ok := infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecret.UID)]; !ok { + if _, ok := resourceVariablesMap[string(infisicalDynamicSecret.UID)]; !ok { ctx, cancel := context.WithCancel(context.Background()) @@ -141,16 +129,16 @@ func (r *InfisicalDynamicSecretReconciler) getResourceVariables(infisicalDynamic UserAgent: api.USER_AGENT_NAME, }) - infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecret.UID)] = util.ResourceVariables{ + resourceVariablesMap[string(infisicalDynamicSecret.UID)] = util.ResourceVariables{ InfisicalClient: client, CancelCtx: cancel, AuthDetails: util.AuthenticationDetails{}, } - resourceVariables = infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecret.UID)] + resourceVariables = resourceVariablesMap[string(infisicalDynamicSecret.UID)] } else { - resourceVariables = infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecret.UID)] + resourceVariables = resourceVariablesMap[string(infisicalDynamicSecret.UID)] } return resourceVariables @@ -253,16 +241,16 @@ func (r *InfisicalDynamicSecretReconciler) RenewDynamicSecretLease(ctx context.C } -func (r *InfisicalDynamicSecretReconciler) updateResourceVariables(infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, resourceVariables util.ResourceVariables) { - infisicalDynamicSecretsResourceVariablesMap[string(infisicalDynamicSecret.UID)] = resourceVariables +func (r *InfisicalDynamicSecretReconciler) updateResourceVariables(infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, resourceVariables util.ResourceVariables, resourceVariablesMap map[string]util.ResourceVariables) { + resourceVariablesMap[string(infisicalDynamicSecret.UID)] = resourceVariables } -func (r *InfisicalDynamicSecretReconciler) HandleLeaseRevocation(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret) error { +func (r *InfisicalDynamicSecretReconciler) HandleLeaseRevocation(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) error { if infisicalDynamicSecret.Spec.LeaseRevocationPolicy != string(constants.DYNAMIC_SECRET_LEASE_REVOCATION_POLICY_ENABLED) { return nil } - resourceVariables := r.getResourceVariables(*infisicalDynamicSecret) + resourceVariables := r.getResourceVariables(*infisicalDynamicSecret, resourceVariablesMap) infisicalClient := resourceVariables.InfisicalClient logger.Info("Authenticating for lease revocation") @@ -276,7 +264,7 @@ func (r *InfisicalDynamicSecretReconciler) HandleLeaseRevocation(ctx context.Con InfisicalClient: infisicalClient, CancelCtx: resourceVariables.CancelCtx, AuthDetails: authDetails, - }) + }, resourceVariablesMap) if infisicalDynamicSecret.Status.Lease == nil { return nil @@ -316,9 +304,9 @@ func (r *InfisicalDynamicSecretReconciler) HandleLeaseRevocation(ctx context.Con return nil } -func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret) (time.Duration, error) { +func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) (time.Duration, error) { - resourceVariables := r.getResourceVariables(*infisicalDynamicSecret) + resourceVariables := r.getResourceVariables(*infisicalDynamicSecret, resourceVariablesMap) infisicalClient := resourceVariables.InfisicalClient cancelCtx := resourceVariables.CancelCtx authDetails := resourceVariables.AuthDetails @@ -331,7 +319,6 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c if authDetails.AuthStrategy == "" { logger.Info("No authentication strategy found. Attempting to authenticate") authDetails, err = r.handleAuthentication(ctx, *infisicalDynamicSecret, infisicalClient) - r.SetAuthenticatedConditionStatus(ctx, logger, infisicalDynamicSecret, err) if err != nil { return nextReconcile, fmt.Errorf("unable to authenticate [err=%s]", err) @@ -341,7 +328,7 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c InfisicalClient: infisicalClient, CancelCtx: cancelCtx, AuthDetails: authDetails, - }) + }, resourceVariablesMap) } destination, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{ @@ -375,7 +362,6 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c if infisicalDynamicSecret.Status.Lease == nil { err := r.CreateDynamicSecretLease(ctx, logger, infisicalClient, infisicalDynamicSecret, destination) - r.SetCreatedLeaseConditionStatus(ctx, logger, infisicalDynamicSecret, err) return defaultNextReconcile, err // Short requeue after creation } else { @@ -413,7 +399,6 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c maxTTLThreshold)) err := r.CreateDynamicSecretLease(ctx, logger, infisicalClient, infisicalDynamicSecret, destination) - r.SetCreatedLeaseConditionStatus(ctx, logger, infisicalDynamicSecret, err) return defaultNextReconcile, err // Short requeue after creation } } @@ -422,7 +407,6 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c if now.After(leaseExpiresAt) { logger.Info("Lease has expired, creating new lease...") err = r.CreateDynamicSecretLease(ctx, logger, infisicalClient, infisicalDynamicSecret, destination) - r.SetCreatedLeaseConditionStatus(ctx, logger, infisicalDynamicSecret, err) return defaultNextReconcile, err // Short requeue after creation } @@ -433,12 +417,10 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c renewalThreshold)) err = r.RenewDynamicSecretLease(ctx, logger, infisicalClient, infisicalDynamicSecret, destination) - r.SetLeaseRenewalConditionStatus(ctx, logger, infisicalDynamicSecret, err) if err == constants.ErrInvalidLease { logger.Info("Failed to renew expired lease, creating new lease...") err = r.CreateDynamicSecretLease(ctx, logger, infisicalClient, infisicalDynamicSecret, destination) - r.SetCreatedLeaseConditionStatus(ctx, logger, infisicalDynamicSecret, err) } return defaultNextReconcile, err // Short requeue after renewal/creation diff --git a/k8-operator/controllers/infisicalpushsecret/conditions.go b/k8-operator/internal/services/infisicalpushsecret/conditions.go similarity index 99% rename from k8-operator/controllers/infisicalpushsecret/conditions.go rename to k8-operator/internal/services/infisicalpushsecret/conditions.go index dd17bc913..57227bd64 100644 --- a/k8-operator/controllers/infisicalpushsecret/conditions.go +++ b/k8-operator/internal/services/infisicalpushsecret/conditions.go @@ -1,4 +1,4 @@ -package controllers +package infisicalpushsecret import ( "context" diff --git a/k8-operator/internal/services/infisicalpushsecret/handler.go b/k8-operator/internal/services/infisicalpushsecret/handler.go new file mode 100644 index 000000000..abe0266d6 --- /dev/null +++ b/k8-operator/internal/services/infisicalpushsecret/handler.go @@ -0,0 +1,99 @@ +package infisicalpushsecret + +import ( + "context" + "fmt" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + "github.com/Infisical/infisical/k8-operator/internal/api" + "github.com/Infisical/infisical/k8-operator/internal/util" + "github.com/go-logr/logr" + k8Errors "k8s.io/apimachinery/pkg/api/errors" +) + +type InfisicalPushSecretHandler struct { + client.Client + Scheme *runtime.Scheme + IsNamespaceScoped bool +} + +func NewInfisicalPushSecretHandler(client client.Client, scheme *runtime.Scheme, isNamespaceScoped bool) *InfisicalPushSecretHandler { + return &InfisicalPushSecretHandler{ + Client: client, + Scheme: scheme, + IsNamespaceScoped: isNamespaceScoped, + } +} + +func (h *InfisicalPushSecretHandler) SetupAPIConfig(infisicalPushSecret v1alpha1.InfisicalPushSecret, infisicalConfig map[string]string) error { + if infisicalPushSecret.Spec.HostAPI == "" { + api.API_HOST_URL = infisicalConfig["hostAPI"] + } else { + api.API_HOST_URL = util.AppendAPIEndpoint(infisicalPushSecret.Spec.HostAPI) + } + return nil +} + +func (h *InfisicalPushSecretHandler) getInfisicalCaCertificateFromKubeSecret(ctx context.Context, infisicalPushSecret v1alpha1.InfisicalPushSecret) (caCertificate string, err error) { + + caCertificateFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, h.Client, types.NamespacedName{ + Namespace: infisicalPushSecret.Spec.TLS.CaRef.SecretNamespace, + Name: infisicalPushSecret.Spec.TLS.CaRef.SecretName, + }) + + if k8Errors.IsNotFound(err) { + return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err) + } + + if err != nil { + return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err) + } + + caCertificateFromSecret := string(caCertificateFromKubeSecret.Data[infisicalPushSecret.Spec.TLS.CaRef.SecretKey]) + + return caCertificateFromSecret, nil +} + +func (h *InfisicalPushSecretHandler) HandleCACertificate(ctx context.Context, infisicalPushSecret v1alpha1.InfisicalPushSecret) error { + if infisicalPushSecret.Spec.TLS.CaRef.SecretName != "" { + caCert, err := h.getInfisicalCaCertificateFromKubeSecret(ctx, infisicalPushSecret) + if err != nil { + return err + } + api.API_CA_CERTIFICATE = caCert + } else { + api.API_CA_CERTIFICATE = "" + } + return nil +} + +func (h *InfisicalPushSecretHandler) ReconcileInfisicalPushSecret(ctx context.Context, logger logr.Logger, infisicalPushSecret *v1alpha1.InfisicalPushSecret, resourceVariablesMap map[string]util.ResourceVariables) error { + reconciler := &InfisicalPushSecretReconciler{ + Client: h.Client, + Scheme: h.Scheme, + IsNamespaceScoped: h.IsNamespaceScoped, + } + return reconciler.ReconcileInfisicalPushSecret(ctx, logger, infisicalPushSecret, resourceVariablesMap) +} + +func (h *InfisicalPushSecretHandler) DeleteManagedSecrets(ctx context.Context, logger logr.Logger, infisicalPushSecret *v1alpha1.InfisicalPushSecret, resourceVariablesMap map[string]util.ResourceVariables) error { + reconciler := &InfisicalPushSecretReconciler{ + Client: h.Client, + Scheme: h.Scheme, + IsNamespaceScoped: h.IsNamespaceScoped, + } + return reconciler.DeleteManagedSecrets(ctx, logger, infisicalPushSecret, resourceVariablesMap) +} + +func (h *InfisicalPushSecretHandler) SetReconcileStatusCondition(ctx context.Context, infisicalPushSecret *v1alpha1.InfisicalPushSecret, err error) { + reconciler := &InfisicalPushSecretReconciler{ + Client: h.Client, + Scheme: h.Scheme, + IsNamespaceScoped: h.IsNamespaceScoped, + } + reconciler.SetReconcileStatusCondition(ctx, infisicalPushSecret, err) +} diff --git a/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_helper.go b/k8-operator/internal/services/infisicalpushsecret/reconciler.go similarity index 85% rename from k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_helper.go rename to k8-operator/internal/services/infisicalpushsecret/reconciler.go index 3cd7d21d7..6c4de31c8 100644 --- a/k8-operator/controllers/infisicalpushsecret/infisicalpushsecret_helper.go +++ b/k8-operator/internal/services/infisicalpushsecret/reconciler.go @@ -1,4 +1,4 @@ -package controllers +package infisicalpushsecret import ( "bytes" @@ -9,21 +9,27 @@ import ( tpl "text/template" "github.com/Infisical/infisical/k8-operator/api/v1alpha1" - "github.com/Infisical/infisical/k8-operator/packages/api" - "github.com/Infisical/infisical/k8-operator/packages/constants" - "github.com/Infisical/infisical/k8-operator/packages/model" - "github.com/Infisical/infisical/k8-operator/packages/template" - "github.com/Infisical/infisical/k8-operator/packages/util" + "github.com/Infisical/infisical/k8-operator/internal/api" + "github.com/Infisical/infisical/k8-operator/internal/constants" + "github.com/Infisical/infisical/k8-operator/internal/model" + "github.com/Infisical/infisical/k8-operator/internal/template" + "github.com/Infisical/infisical/k8-operator/internal/util" "github.com/go-logr/logr" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" - generatorUtil "github.com/Infisical/infisical/k8-operator/packages/generator" + generatorUtil "github.com/Infisical/infisical/k8-operator/internal/generator" infisicalSdk "github.com/infisical/go-sdk" - k8Errors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" ) +type InfisicalPushSecretReconciler struct { + client.Client + Scheme *runtime.Scheme + IsNamespaceScoped bool +} + func (r *InfisicalPushSecretReconciler) handleAuthentication(ctx context.Context, infisicalSecret v1alpha1.InfisicalPushSecret, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error) { authStrategies := map[util.AuthStrategyType]func(ctx context.Context, reconcilerClient client.Client, secretCrd util.SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error){ util.AuthStrategy.UNIVERSAL_MACHINE_IDENTITY: util.HandleUniversalAuth, @@ -54,31 +60,11 @@ func (r *InfisicalPushSecretReconciler) handleAuthentication(ctx context.Context } -func (r *InfisicalPushSecretReconciler) getInfisicalCaCertificateFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalPushSecret) (caCertificate string, err error) { - - caCertificateFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{ - Namespace: infisicalSecret.Spec.TLS.CaRef.SecretNamespace, - Name: infisicalSecret.Spec.TLS.CaRef.SecretName, - }) - - if k8Errors.IsNotFound(err) { - return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err) - } - - if err != nil { - return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err) - } - - caCertificateFromSecret := string(caCertificateFromKubeSecret.Data[infisicalSecret.Spec.TLS.CaRef.SecretKey]) - - return caCertificateFromSecret, nil -} - -func (r *InfisicalPushSecretReconciler) getResourceVariables(infisicalPushSecret v1alpha1.InfisicalPushSecret) util.ResourceVariables { +func (r *InfisicalPushSecretReconciler) getResourceVariables(infisicalPushSecret v1alpha1.InfisicalPushSecret, resourceVariablesMap map[string]util.ResourceVariables) util.ResourceVariables { var resourceVariables util.ResourceVariables - if _, ok := infisicalPushSecretResourceVariablesMap[string(infisicalPushSecret.UID)]; !ok { + if _, ok := resourceVariablesMap[string(infisicalPushSecret.UID)]; !ok { ctx, cancel := context.WithCancel(context.Background()) @@ -88,24 +74,24 @@ func (r *InfisicalPushSecretReconciler) getResourceVariables(infisicalPushSecret UserAgent: api.USER_AGENT_NAME, }) - infisicalPushSecretResourceVariablesMap[string(infisicalPushSecret.UID)] = util.ResourceVariables{ + resourceVariablesMap[string(infisicalPushSecret.UID)] = util.ResourceVariables{ InfisicalClient: client, CancelCtx: cancel, AuthDetails: util.AuthenticationDetails{}, } - resourceVariables = infisicalPushSecretResourceVariablesMap[string(infisicalPushSecret.UID)] + resourceVariables = resourceVariablesMap[string(infisicalPushSecret.UID)] } else { - resourceVariables = infisicalPushSecretResourceVariablesMap[string(infisicalPushSecret.UID)] + resourceVariables = resourceVariablesMap[string(infisicalPushSecret.UID)] } return resourceVariables } -func (r *InfisicalPushSecretReconciler) updateResourceVariables(infisicalPushSecret v1alpha1.InfisicalPushSecret, resourceVariables util.ResourceVariables) { - infisicalPushSecretResourceVariablesMap[string(infisicalPushSecret.UID)] = resourceVariables +func (r *InfisicalPushSecretReconciler) updateResourceVariables(infisicalPushSecret v1alpha1.InfisicalPushSecret, resourceVariables util.ResourceVariables, resourceVariablesMap map[string]util.ResourceVariables) { + resourceVariablesMap[string(infisicalPushSecret.UID)] = resourceVariables } func (r *InfisicalPushSecretReconciler) processGenerators(ctx context.Context, infisicalPushSecret v1alpha1.InfisicalPushSecret) (map[string]string, error) { @@ -196,9 +182,9 @@ func (r *InfisicalPushSecretReconciler) processTemplatedSecrets(infisicalPushSec return processedSecrets, nil } -func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context.Context, logger logr.Logger, infisicalPushSecret v1alpha1.InfisicalPushSecret) error { +func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context.Context, logger logr.Logger, infisicalPushSecret *v1alpha1.InfisicalPushSecret, resourceVariablesMap map[string]util.ResourceVariables) error { - resourceVariables := r.getResourceVariables(infisicalPushSecret) + resourceVariables := r.getResourceVariables(*infisicalPushSecret, resourceVariablesMap) infisicalClient := resourceVariables.InfisicalClient cancelCtx := resourceVariables.CancelCtx authDetails := resourceVariables.AuthDetails @@ -206,18 +192,18 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context if authDetails.AuthStrategy == "" { logger.Info("No authentication strategy found. Attempting to authenticate") - authDetails, err = r.handleAuthentication(ctx, infisicalPushSecret, infisicalClient) - r.SetAuthenticatedStatusCondition(ctx, &infisicalPushSecret, err) + authDetails, err = r.handleAuthentication(ctx, *infisicalPushSecret, infisicalClient) + r.SetAuthenticatedStatusCondition(ctx, infisicalPushSecret, err) if err != nil { return fmt.Errorf("unable to authenticate [err=%s]", err) } - r.updateResourceVariables(infisicalPushSecret, util.ResourceVariables{ + r.updateResourceVariables(*infisicalPushSecret, util.ResourceVariables{ InfisicalClient: infisicalClient, CancelCtx: cancelCtx, AuthDetails: authDetails, - }) + }, resourceVariablesMap) } processedSecrets := make(map[string]string) @@ -232,13 +218,13 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context return fmt.Errorf("unable to fetch kube secret [err=%s]", err) } - processedSecrets, err = r.processTemplatedSecrets(infisicalPushSecret, kubePushSecret, infisicalPushSecret.Spec.Destination) + processedSecrets, err = r.processTemplatedSecrets(*infisicalPushSecret, kubePushSecret, infisicalPushSecret.Spec.Destination) if err != nil { return fmt.Errorf("unable to process templated secrets [err=%s]", err) } } - generatorSecrets, err := r.processGenerators(ctx, infisicalPushSecret) + generatorSecrets, err := r.processGenerators(ctx, *infisicalPushSecret) if err != nil { return fmt.Errorf("unable to process generators [err=%s]", err) } @@ -508,31 +494,31 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context } else { errorMessage = "" } - r.SetFailedToCreateSecretsStatusCondition(ctx, &infisicalPushSecret, fmt.Sprintf("Failed to create secrets: [%s]", errorMessage)) + r.SetFailedToCreateSecretsStatusCondition(ctx, infisicalPushSecret, fmt.Sprintf("Failed to create secrets: [%s]", errorMessage)) if len(secretsFailedToUpdate) > 0 { errorMessage = fmt.Sprintf("Failed to update secrets: [%s]", strings.Join(secretsFailedToUpdate, ", ")) } else { errorMessage = "" } - r.SetFailedToUpdateSecretsStatusCondition(ctx, &infisicalPushSecret, fmt.Sprintf("Failed to update secrets: [%s]", errorMessage)) + r.SetFailedToUpdateSecretsStatusCondition(ctx, infisicalPushSecret, fmt.Sprintf("Failed to update secrets: [%s]", errorMessage)) if len(secretsFailedToDelete) > 0 { errorMessage = fmt.Sprintf("Failed to delete secrets: [%s]", strings.Join(secretsFailedToDelete, ", ")) } else { errorMessage = "" } - r.SetFailedToDeleteSecretsStatusCondition(ctx, &infisicalPushSecret, errorMessage) + r.SetFailedToDeleteSecretsStatusCondition(ctx, infisicalPushSecret, errorMessage) if len(secretsFailedToReplaceById) > 0 { errorMessage = fmt.Sprintf("Failed to replace secrets: [%s]", strings.Join(secretsFailedToReplaceById, ", ")) } else { errorMessage = "" } - r.SetFailedToReplaceSecretsStatusCondition(ctx, &infisicalPushSecret, errorMessage) + r.SetFailedToReplaceSecretsStatusCondition(ctx, infisicalPushSecret, errorMessage) // Update the status of the InfisicalPushSecret - if err := r.Client.Status().Update(ctx, &infisicalPushSecret); err != nil { + if err := r.Client.Status().Update(ctx, infisicalPushSecret); err != nil { return fmt.Errorf("unable to update status of InfisicalPushSecret [err=%s]", err) } @@ -540,12 +526,12 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context } -func (r *InfisicalPushSecretReconciler) DeleteManagedSecrets(ctx context.Context, logger logr.Logger, infisicalPushSecret v1alpha1.InfisicalPushSecret) error { +func (r *InfisicalPushSecretReconciler) DeleteManagedSecrets(ctx context.Context, logger logr.Logger, infisicalPushSecret *v1alpha1.InfisicalPushSecret, resourceVariablesMap map[string]util.ResourceVariables) error { if infisicalPushSecret.Spec.DeletionPolicy != string(constants.PUSH_SECRET_DELETE_POLICY_ENABLED) { return nil } - resourceVariables := r.getResourceVariables(infisicalPushSecret) + resourceVariables := r.getResourceVariables(*infisicalPushSecret, resourceVariablesMap) infisicalClient := resourceVariables.InfisicalClient cancelCtx := resourceVariables.CancelCtx authDetails := resourceVariables.AuthDetails @@ -553,18 +539,18 @@ func (r *InfisicalPushSecretReconciler) DeleteManagedSecrets(ctx context.Context if authDetails.AuthStrategy == "" { logger.Info("No authentication strategy found. Attempting to authenticate") - authDetails, err = r.handleAuthentication(ctx, infisicalPushSecret, infisicalClient) - r.SetAuthenticatedStatusCondition(ctx, &infisicalPushSecret, err) + authDetails, err = r.handleAuthentication(ctx, *infisicalPushSecret, infisicalClient) + r.SetAuthenticatedStatusCondition(ctx, infisicalPushSecret, err) if err != nil { return fmt.Errorf("unable to authenticate [err=%s]", err) } - r.updateResourceVariables(infisicalPushSecret, util.ResourceVariables{ + r.updateResourceVariables(*infisicalPushSecret, util.ResourceVariables{ InfisicalClient: infisicalClient, CancelCtx: cancelCtx, AuthDetails: authDetails, - }) + }, resourceVariablesMap) } destination := infisicalPushSecret.Spec.Destination diff --git a/k8-operator/k8-operator/internal/services/infisicalsecret/conditions.go b/k8-operator/internal/services/infisicalsecret/conditions.go similarity index 100% rename from k8-operator/k8-operator/internal/services/infisicalsecret/conditions.go rename to k8-operator/internal/services/infisicalsecret/conditions.go diff --git a/k8-operator/k8-operator/internal/services/infisicalsecret/handler.go b/k8-operator/internal/services/infisicalsecret/handler.go similarity index 100% rename from k8-operator/k8-operator/internal/services/infisicalsecret/handler.go rename to k8-operator/internal/services/infisicalsecret/handler.go diff --git a/k8-operator/k8-operator/internal/services/infisicalsecret/reconciler.go b/k8-operator/internal/services/infisicalsecret/reconciler.go similarity index 100% rename from k8-operator/k8-operator/internal/services/infisicalsecret/reconciler.go rename to k8-operator/internal/services/infisicalsecret/reconciler.go diff --git a/k8-operator/k8-operator/internal/services/infisicalsecret/suite_test.go b/k8-operator/internal/services/infisicalsecret/suite_test.go similarity index 100% rename from k8-operator/k8-operator/internal/services/infisicalsecret/suite_test.go rename to k8-operator/internal/services/infisicalsecret/suite_test.go diff --git a/k8-operator/k8-operator/internal/template/base64.go b/k8-operator/internal/template/base64.go similarity index 100% rename from k8-operator/k8-operator/internal/template/base64.go rename to k8-operator/internal/template/base64.go diff --git a/k8-operator/k8-operator/internal/template/jwk.go b/k8-operator/internal/template/jwk.go similarity index 100% rename from k8-operator/k8-operator/internal/template/jwk.go rename to k8-operator/internal/template/jwk.go diff --git a/k8-operator/k8-operator/internal/template/pem.go b/k8-operator/internal/template/pem.go similarity index 100% rename from k8-operator/k8-operator/internal/template/pem.go rename to k8-operator/internal/template/pem.go diff --git a/k8-operator/k8-operator/internal/template/pem_chain.go b/k8-operator/internal/template/pem_chain.go similarity index 100% rename from k8-operator/k8-operator/internal/template/pem_chain.go rename to k8-operator/internal/template/pem_chain.go diff --git a/k8-operator/k8-operator/internal/template/pkcs12.go b/k8-operator/internal/template/pkcs12.go similarity index 100% rename from k8-operator/k8-operator/internal/template/pkcs12.go rename to k8-operator/internal/template/pkcs12.go diff --git a/k8-operator/k8-operator/internal/template/template.go b/k8-operator/internal/template/template.go similarity index 100% rename from k8-operator/k8-operator/internal/template/template.go rename to k8-operator/internal/template/template.go diff --git a/k8-operator/k8-operator/internal/template/yaml.go b/k8-operator/internal/template/yaml.go similarity index 100% rename from k8-operator/k8-operator/internal/template/yaml.go rename to k8-operator/internal/template/yaml.go diff --git a/k8-operator/packages/util/auth.go b/k8-operator/internal/util/auth.go similarity index 100% rename from k8-operator/packages/util/auth.go rename to k8-operator/internal/util/auth.go diff --git a/k8-operator/k8-operator/internal/util/helpers.go b/k8-operator/internal/util/helpers.go similarity index 100% rename from k8-operator/k8-operator/internal/util/helpers.go rename to k8-operator/internal/util/helpers.go diff --git a/k8-operator/k8-operator/internal/util/kubernetes.go b/k8-operator/internal/util/kubernetes.go similarity index 100% rename from k8-operator/k8-operator/internal/util/kubernetes.go rename to k8-operator/internal/util/kubernetes.go diff --git a/k8-operator/k8-operator/internal/util/models.go b/k8-operator/internal/util/models.go similarity index 100% rename from k8-operator/k8-operator/internal/util/models.go rename to k8-operator/internal/util/models.go diff --git a/k8-operator/k8-operator/internal/util/secrets.go b/k8-operator/internal/util/secrets.go similarity index 100% rename from k8-operator/k8-operator/internal/util/secrets.go rename to k8-operator/internal/util/secrets.go diff --git a/k8-operator/k8-operator/internal/util/time.go b/k8-operator/internal/util/time.go similarity index 100% rename from k8-operator/k8-operator/internal/util/time.go rename to k8-operator/internal/util/time.go diff --git a/k8-operator/k8-operator/internal/util/workspace.go b/k8-operator/internal/util/workspace.go similarity index 100% rename from k8-operator/k8-operator/internal/util/workspace.go rename to k8-operator/internal/util/workspace.go diff --git a/k8-operator/k8-operator/.devcontainer/devcontainer.json b/k8-operator/k8-operator/.devcontainer/devcontainer.json deleted file mode 100644 index a3ab7541c..000000000 --- a/k8-operator/k8-operator/.devcontainer/devcontainer.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "name": "Kubebuilder DevContainer", - "image": "golang:1.24", - "features": { - "ghcr.io/devcontainers/features/docker-in-docker:2": {}, - "ghcr.io/devcontainers/features/git:1": {} - }, - - "runArgs": ["--network=host"], - - "customizations": { - "vscode": { - "settings": { - "terminal.integrated.shell.linux": "/bin/bash" - }, - "extensions": [ - "ms-kubernetes-tools.vscode-kubernetes-tools", - "ms-azuretools.vscode-docker" - ] - } - }, - - "onCreateCommand": "bash .devcontainer/post-install.sh" -} - diff --git a/k8-operator/k8-operator/.devcontainer/post-install.sh b/k8-operator/k8-operator/.devcontainer/post-install.sh deleted file mode 100644 index 265c43ee8..000000000 --- a/k8-operator/k8-operator/.devcontainer/post-install.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash -set -x - -curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 -chmod +x ./kind -mv ./kind /usr/local/bin/kind - -curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/linux/amd64 -chmod +x kubebuilder -mv kubebuilder /usr/local/bin/ - -KUBECTL_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt) -curl -LO "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/amd64/kubectl" -chmod +x kubectl -mv kubectl /usr/local/bin/kubectl - -docker network create -d=bridge --subnet=172.19.0.0/24 kind - -kind version -kubebuilder version -docker --version -go version -kubectl version --client diff --git a/k8-operator/k8-operator/.dockerignore b/k8-operator/k8-operator/.dockerignore deleted file mode 100644 index a3aab7af7..000000000 --- a/k8-operator/k8-operator/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -# More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file -# Ignore build and test binaries. -bin/ diff --git a/k8-operator/k8-operator/.github/workflows/lint.yml b/k8-operator/k8-operator/.github/workflows/lint.yml deleted file mode 100644 index 67ff2bf09..000000000 --- a/k8-operator/k8-operator/.github/workflows/lint.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Lint - -on: - push: - pull_request: - -jobs: - lint: - name: Run on Ubuntu - runs-on: ubuntu-latest - steps: - - name: Clone the code - uses: actions/checkout@v4 - - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version-file: go.mod - - - name: Run linter - uses: golangci/golangci-lint-action@v8 - with: - version: v2.1.6 diff --git a/k8-operator/k8-operator/.github/workflows/test-e2e.yml b/k8-operator/k8-operator/.github/workflows/test-e2e.yml deleted file mode 100644 index 68fd1ed55..000000000 --- a/k8-operator/k8-operator/.github/workflows/test-e2e.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: E2E Tests - -on: - push: - pull_request: - -jobs: - test-e2e: - name: Run on Ubuntu - runs-on: ubuntu-latest - steps: - - name: Clone the code - uses: actions/checkout@v4 - - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version-file: go.mod - - - name: Install the latest version of kind - run: | - curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 - chmod +x ./kind - sudo mv ./kind /usr/local/bin/kind - - - name: Verify kind installation - run: kind version - - - name: Running Test e2e - run: | - go mod tidy - make test-e2e diff --git a/k8-operator/k8-operator/.github/workflows/test.yml b/k8-operator/k8-operator/.github/workflows/test.yml deleted file mode 100644 index fc2e80d30..000000000 --- a/k8-operator/k8-operator/.github/workflows/test.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Tests - -on: - push: - pull_request: - -jobs: - test: - name: Run on Ubuntu - runs-on: ubuntu-latest - steps: - - name: Clone the code - uses: actions/checkout@v4 - - - name: Setup Go - uses: actions/setup-go@v5 - with: - go-version-file: go.mod - - - name: Running Tests - run: | - go mod tidy - make test diff --git a/k8-operator/k8-operator/.gitignore b/k8-operator/k8-operator/.gitignore deleted file mode 100644 index ada68ff08..000000000 --- a/k8-operator/k8-operator/.gitignore +++ /dev/null @@ -1,27 +0,0 @@ -# Binaries for programs and plugins -*.exe -*.exe~ -*.dll -*.so -*.dylib -bin/* -Dockerfile.cross - -# Test binary, built with `go test -c` -*.test - -# Output of the go coverage tool, specifically when used with LiteIDE -*.out - -# Go workspace file -go.work - -# Kubernetes Generated files - skip generated files, except for vendored files -!vendor/**/zz_generated.* - -# editor and IDE paraphernalia -.idea -.vscode -*.swp -*.swo -*~ diff --git a/k8-operator/k8-operator/.golangci.yml b/k8-operator/k8-operator/.golangci.yml deleted file mode 100644 index e5b21b0f1..000000000 --- a/k8-operator/k8-operator/.golangci.yml +++ /dev/null @@ -1,52 +0,0 @@ -version: "2" -run: - allow-parallel-runners: true -linters: - default: none - enable: - - copyloopvar - - dupl - - errcheck - - ginkgolinter - - goconst - - gocyclo - - govet - - ineffassign - - lll - - misspell - - nakedret - - prealloc - - revive - - staticcheck - - unconvert - - unparam - - unused - settings: - revive: - rules: - - name: comment-spacings - - name: import-shadowing - exclusions: - generated: lax - rules: - - linters: - - lll - path: api/* - - linters: - - dupl - - lll - path: internal/* - paths: - - third_party$ - - builtin$ - - examples$ -formatters: - enable: - - gofmt - - goimports - exclusions: - generated: lax - paths: - - third_party$ - - builtin$ - - examples$ diff --git a/k8-operator/k8-operator/Dockerfile b/k8-operator/k8-operator/Dockerfile deleted file mode 100644 index cb1b130fd..000000000 --- a/k8-operator/k8-operator/Dockerfile +++ /dev/null @@ -1,33 +0,0 @@ -# Build the manager binary -FROM golang:1.24 AS builder -ARG TARGETOS -ARG TARGETARCH - -WORKDIR /workspace -# Copy the Go Modules manifests -COPY go.mod go.mod -COPY go.sum go.sum -# 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 the go source -COPY cmd/main.go cmd/main.go -COPY api/ api/ -COPY internal/ internal/ - -# Build -# the GOARCH has not a default value to allow the binary be built according to the host where the command -# was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO -# the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, -# by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. -RUN CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/main.go - -# Use distroless as minimal base image to package the manager binary -# Refer to https://github.com/GoogleContainerTools/distroless for more details -FROM gcr.io/distroless/static:nonroot -WORKDIR / -COPY --from=builder /workspace/manager . -USER 65532:65532 - -ENTRYPOINT ["/manager"] diff --git a/k8-operator/k8-operator/Makefile b/k8-operator/k8-operator/Makefile deleted file mode 100644 index b776cf7a9..000000000 --- a/k8-operator/k8-operator/Makefile +++ /dev/null @@ -1,238 +0,0 @@ -# Image URL to use all building/pushing image targets -IMG ?= controller: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 - -# CONTAINER_TOOL defines the container tool to be used for building images. -# Be aware that the target commands are only tested with Docker which is -# scaffolded by default. However, you might want to replace it to use other -# tools. (i.e. podman) -CONTAINER_TOOL ?= docker - -# Setting SHELL to bash allows bash commands to be executed by recipes. -# 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: build - -##@ 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 command 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: help -help: ## Display this help. - @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) - -##@ Development - -.PHONY: manifests -manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. - $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases - -.PHONY: generate -generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. - $(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..." - -.PHONY: fmt -fmt: ## Run go fmt against code. - go fmt ./... - -.PHONY: vet -vet: ## Run go vet against code. - go vet ./... - -.PHONY: test -test: manifests generate fmt vet setup-envtest ## Run tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out - -# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. -# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. -# CertManager is installed by default; skip with: -# - CERT_MANAGER_INSTALL_SKIP=true -KIND_CLUSTER ?= k8-operator-test-e2e - -.PHONY: setup-test-e2e -setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist - @command -v $(KIND) >/dev/null 2>&1 || { \ - echo "Kind is not installed. Please install Kind manually."; \ - exit 1; \ - } - @case "$$($(KIND) get clusters)" in \ - *"$(KIND_CLUSTER)"*) \ - echo "Kind cluster '$(KIND_CLUSTER)' already exists. Skipping creation." ;; \ - *) \ - echo "Creating Kind cluster '$(KIND_CLUSTER)'..."; \ - $(KIND) create cluster --name $(KIND_CLUSTER) ;; \ - esac - -.PHONY: test-e2e -test-e2e: setup-test-e2e manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind. - KIND_CLUSTER=$(KIND_CLUSTER) go test ./test/e2e/ -v -ginkgo.v - $(MAKE) cleanup-test-e2e - -.PHONY: cleanup-test-e2e -cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests - @$(KIND) delete cluster --name $(KIND_CLUSTER) - -.PHONY: lint -lint: golangci-lint ## Run golangci-lint linter - $(GOLANGCI_LINT) run - -.PHONY: lint-fix -lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes - $(GOLANGCI_LINT) run --fix - -.PHONY: lint-config -lint-config: golangci-lint ## Verify golangci-lint linter configuration - $(GOLANGCI_LINT) config verify - -##@ Build - -.PHONY: build -build: manifests generate fmt vet ## Build manager binary. - go build -o bin/manager cmd/main.go - -.PHONY: run -run: manifests generate fmt vet ## Run a controller from your host. - go run ./cmd/main.go - -# If you wish to build the manager image targeting other platforms you can use the --platform flag. -# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. -# More info: https://docs.docker.com/develop/develop-images/build_enhancements/ -.PHONY: docker-build -docker-build: ## Build docker image with the manager. - $(CONTAINER_TOOL) build -t ${IMG} . - -.PHONY: docker-push -docker-push: ## Push docker image with the manager. - $(CONTAINER_TOOL) push ${IMG} - -# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple -# architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: -# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/ -# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/ -# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=> then the export will fail) -# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option. -PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le -.PHONY: docker-buildx -docker-buildx: ## Build and push docker image for the manager for cross-platform support - # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile - sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross - - $(CONTAINER_TOOL) buildx create --name k8-operator-builder - $(CONTAINER_TOOL) buildx use k8-operator-builder - - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . - - $(CONTAINER_TOOL) buildx rm k8-operator-builder - rm Dockerfile.cross - -.PHONY: build-installer -build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment. - mkdir -p dist - cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} - $(KUSTOMIZE) build config/default > dist/install.yaml - -##@ Deployment - -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: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config. - cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} - $(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - - -.PHONY: undeploy -undeploy: kustomize ## 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 - - -##@ Dependencies - -## Location to install dependencies to -LOCALBIN ?= $(shell pwd)/bin -$(LOCALBIN): - mkdir -p $(LOCALBIN) - -## Tool Binaries -KUBECTL ?= kubectl -KIND ?= kind -KUSTOMIZE ?= $(LOCALBIN)/kustomize -CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen -ENVTEST ?= $(LOCALBIN)/setup-envtest -GOLANGCI_LINT = $(LOCALBIN)/golangci-lint - -## Tool Versions -KUSTOMIZE_VERSION ?= v5.6.0 -CONTROLLER_TOOLS_VERSION ?= v0.18.0 -#ENVTEST_VERSION is the version of controller-runtime release branch to fetch the envtest setup script (i.e. release-0.20) -ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}') -#ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31) -ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d", $$3}') -GOLANGCI_LINT_VERSION ?= v2.1.6 - -.PHONY: kustomize -kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. -$(KUSTOMIZE): $(LOCALBIN) - $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) - -.PHONY: controller-gen -controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. -$(CONTROLLER_GEN): $(LOCALBIN) - $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) - -.PHONY: setup-envtest -setup-envtest: envtest ## Download the binaries required for ENVTEST in the local bin directory. - @echo "Setting up envtest binaries for Kubernetes version $(ENVTEST_K8S_VERSION)..." - @$(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path || { \ - echo "Error: Failed to set up envtest binaries for version $(ENVTEST_K8S_VERSION)."; \ - exit 1; \ - } - -.PHONY: envtest -envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. -$(ENVTEST): $(LOCALBIN) - $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION)) - -.PHONY: golangci-lint -golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. -$(GOLANGCI_LINT): $(LOCALBIN) - $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) - -# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist -# $1 - target path with name of binary -# $2 - package url which can be installed -# $3 - specific version of package -define go-install-tool -@[ -f "$(1)-$(3)" ] || { \ -set -e; \ -package=$(2)@$(3) ;\ -echo "Downloading $${package}" ;\ -rm -f $(1) || true ;\ -GOBIN=$(LOCALBIN) go install $${package} ;\ -mv $(1) $(1)-$(3) ;\ -} ;\ -ln -sf $(1)-$(3) $(1) -endef diff --git a/k8-operator/k8-operator/PROJECT b/k8-operator/k8-operator/PROJECT deleted file mode 100644 index dc9260f24..000000000 --- a/k8-operator/k8-operator/PROJECT +++ /dev/null @@ -1,39 +0,0 @@ -# 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 -cliVersion: 4.7.0 -domain: infisical.com -layout: -- go.kubebuilder.io/v4 -projectName: k8-operator -repo: github.com/Infisical/infisical/k8-operator -resources: -- api: - crdVersion: v1 - namespaced: true - controller: true - domain: infisical.com - group: secrets - kind: InfisicalSecret - path: github.com/Infisical/infisical/k8-operator/api/v1alpha1 - version: v1alpha1 -- api: - crdVersion: v1 - namespaced: true - controller: true - domain: infisical.com - group: secrets - kind: InfisicalPushSecretSecret - path: github.com/Infisical/infisical/k8-operator/api/v1alpha1 - version: v1alpha1 -- api: - crdVersion: v1 - namespaced: true - controller: true - domain: infisical.com - group: secrets - kind: InfisicalDynamicSecret - path: github.com/Infisical/infisical/k8-operator/api/v1alpha1 - version: v1alpha1 -version: "3" diff --git a/k8-operator/k8-operator/README.md b/k8-operator/k8-operator/README.md deleted file mode 100644 index e5cab2089..000000000 --- a/k8-operator/k8-operator/README.md +++ /dev/null @@ -1,135 +0,0 @@ -# k8-operator -// TODO(user): Add simple overview of use/purpose - -## Description -// TODO(user): An in-depth paragraph about your project and overview of use - -## Getting Started - -### Prerequisites -- go version v1.24.0+ -- docker version 17.03+. -- kubectl version v1.11.3+. -- Access to a Kubernetes v1.11.3+ cluster. - -### To Deploy on the cluster -**Build and push your image to the location specified by `IMG`:** - -```sh -make docker-build docker-push IMG=/k8-operator:tag -``` - -**NOTE:** This image ought to be published in the personal registry you specified. -And it is required to have access to pull the image from the working environment. -Make sure you have the proper permission to the registry if the above commands don’t work. - -**Install the CRDs into the cluster:** - -```sh -make install -``` - -**Deploy the Manager to the cluster with the image specified by `IMG`:** - -```sh -make deploy IMG=/k8-operator:tag -``` - -> **NOTE**: If you encounter RBAC errors, you may need to grant yourself cluster-admin -privileges or be logged in as admin. - -**Create instances of your solution** -You can apply the samples (examples) from the config/sample: - -```sh -kubectl apply -k config/samples/ -``` - ->**NOTE**: Ensure that the samples has default values to test it out. - -### To Uninstall -**Delete the instances (CRs) from the cluster:** - -```sh -kubectl delete -k config/samples/ -``` - -**Delete the APIs(CRDs) from the cluster:** - -```sh -make uninstall -``` - -**UnDeploy the controller from the cluster:** - -```sh -make undeploy -``` - -## Project Distribution - -Following the options to release and provide this solution to the users. - -### By providing a bundle with all YAML files - -1. Build the installer for the image built and published in the registry: - -```sh -make build-installer IMG=/k8-operator:tag -``` - -**NOTE:** The makefile target mentioned above generates an 'install.yaml' -file in the dist directory. This file contains all the resources built -with Kustomize, which are necessary to install this project without its -dependencies. - -2. Using the installer - -Users can just run 'kubectl apply -f ' to install -the project, i.e.: - -```sh -kubectl apply -f https://raw.githubusercontent.com//k8-operator//dist/install.yaml -``` - -### By providing a Helm Chart - -1. Build the chart using the optional helm plugin - -```sh -kubebuilder edit --plugins=helm/v1-alpha -``` - -2. See that a chart was generated under 'dist/chart', and users -can obtain this solution from there. - -**NOTE:** If you change the project, you need to update the Helm Chart -using the same command above to sync the latest changes. Furthermore, -if you create webhooks, you need to use the above command with -the '--force' flag and manually ensure that any custom configuration -previously added to 'dist/chart/values.yaml' or 'dist/chart/manager/manager.yaml' -is manually re-applied afterwards. - -## Contributing -// TODO(user): Add detailed information on how you would like others to contribute to this project - -**NOTE:** Run `make help` for more information on all potential `make` targets - -More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html) - -## License - -Copyright 2025. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - diff --git a/k8-operator/k8-operator/api/v1alpha1/common.go b/k8-operator/k8-operator/api/v1alpha1/common.go deleted file mode 100644 index 2362857d8..000000000 --- a/k8-operator/k8-operator/api/v1alpha1/common.go +++ /dev/null @@ -1,149 +0,0 @@ -package v1alpha1 - -type GenericInfisicalAuthentication struct { - // +kubebuilder:validation:Optional - UniversalAuth GenericUniversalAuth `json:"universalAuth,omitempty"` - // +kubebuilder:validation:Optional - KubernetesAuth GenericKubernetesAuth `json:"kubernetesAuth,omitempty"` - // +kubebuilder:validation:Optional - AwsIamAuth GenericAwsIamAuth `json:"awsIamAuth,omitempty"` - // +kubebuilder:validation:Optional - AzureAuth GenericAzureAuth `json:"azureAuth,omitempty"` - // +kubebuilder:validation:Optional - GcpIdTokenAuth GenericGcpIdTokenAuth `json:"gcpIdTokenAuth,omitempty"` - // +kubebuilder:validation:Optional - GcpIamAuth GenericGcpIamAuth `json:"gcpIamAuth,omitempty"` -} - -type GenericUniversalAuth struct { - // +kubebuilder:validation:Required - CredentialsRef KubeSecretReference `json:"credentialsRef"` -} - -type GenericAwsIamAuth struct { - // +kubebuilder:validation:Required - IdentityID string `json:"identityId"` -} - -type GenericAzureAuth struct { - // +kubebuilder:validation:Required - IdentityID string `json:"identityId"` - // +kubebuilder:validation:Optional - Resource string `json:"resource,omitempty"` -} - -type GenericGcpIdTokenAuth struct { - // +kubebuilder:validation:Required - IdentityID string `json:"identityId"` -} - -type GenericGcpIamAuth struct { - // +kubebuilder:validation:Required - IdentityID string `json:"identityId"` - // +kubebuilder:validation:Required - ServiceAccountKeyFilePath string `json:"serviceAccountKeyFilePath"` -} - -type GenericKubernetesAuth struct { - // +kubebuilder:validation:Required - IdentityID string `json:"identityId"` - // +kubebuilder:validation:Required - ServiceAccountRef KubernetesServiceAccountRef `json:"serviceAccountRef"` - - // Optionally automatically create a service account token for the configured service account. - // If this is set to `true`, the operator will automatically create a service account token for the configured service account. This field is recommended in most cases. - // +kubebuilder:validation:Optional - AutoCreateServiceAccountToken bool `json:"autoCreateServiceAccountToken"` - // The audiences to use for the service account token. This is only relevant if `autoCreateServiceAccountToken` is true. - // +kubebuilder:validation:Optional - ServiceAccountTokenAudiences []string `json:"serviceAccountTokenAudiences"` -} - -type TLSConfig struct { - // Reference to secret containing CA cert - // +kubebuilder:validation:Optional - CaRef CaReference `json:"caRef,omitempty"` -} - -type CaReference struct { - // The name of the Kubernetes Secret - // +kubebuilder:validation:Required - SecretName string `json:"secretName"` - - // The namespace where the Kubernetes Secret is located - // +kubebuilder:validation:Required - SecretNamespace string `json:"secretNamespace"` - - // +kubebuilder:validation:Required - // The name of the secret property with the CA certificate value - SecretKey string `json:"key"` -} - -type KubeSecretReference struct { - // The name of the Kubernetes Secret - // +kubebuilder:validation:Required - SecretName string `json:"secretName"` - - // The name space where the Kubernetes Secret is located - // +kubebuilder:validation:Required - SecretNamespace string `json:"secretNamespace"` -} - -type ManagedKubeSecretConfig struct { - // The name of the Kubernetes Secret - // +kubebuilder:validation:Required - SecretName string `json:"secretName"` - - // The name space where the Kubernetes Secret is located - // +kubebuilder:validation:Required - SecretNamespace string `json:"secretNamespace"` - - // The Kubernetes Secret type (experimental feature). More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types - // +kubebuilder:validation:Optional - // +kubebuilder:default:=Opaque - SecretType string `json:"secretType"` - - // The Kubernetes Secret creation policy. - // Enum with values: 'Owner', 'Orphan'. - // Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. - // Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted. - // +kubebuilder:validation:Optional - // +kubebuilder:default:=Orphan - CreationPolicy string `json:"creationPolicy"` - - // The template to transform the secret data - // +kubebuilder:validation:Optional - Template *SecretTemplate `json:"template,omitempty"` -} - -type ManagedKubeConfigMapConfig struct { - // The name of the Kubernetes ConfigMap - // +kubebuilder:validation:Required - ConfigMapName string `json:"configMapName"` - - // The Kubernetes ConfigMap creation policy. - // Enum with values: 'Owner', 'Orphan'. - // Owner creates the config map and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. - // Orphan will not set the config map owner. This will result in the config map being orphaned and not deleted when the resource is deleted. - // +kubebuilder:validation:Optional - // +kubebuilder:default:=Orphan - CreationPolicy string `json:"creationPolicy"` - - // The namespace where the Kubernetes ConfigMap is located - // +kubebuilder:validation:Required - ConfigMapNamespace string `json:"configMapNamespace"` - - // The template to transform the secret data - // +kubebuilder:validation:Optional - Template *SecretTemplate `json:"template,omitempty"` -} - -type SecretTemplate struct { - // This injects all retrieved secrets into the top level of your template. - // Secrets defined in the template will take precedence over the injected ones. - // +kubebuilder:validation:Optional - IncludeAllSecrets bool `json:"includeAllSecrets"` - // The template key values - // +kubebuilder:validation:Optional - Data map[string]string `json:"data,omitempty"` -} diff --git a/k8-operator/k8-operator/api/v1alpha1/generators.go b/k8-operator/k8-operator/api/v1alpha1/generators.go deleted file mode 100644 index 0f6d86c2d..000000000 --- a/k8-operator/k8-operator/api/v1alpha1/generators.go +++ /dev/null @@ -1,152 +0,0 @@ -/* -Copyright 2022. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// GeneratorKind represents a kind of generator. -// +kubebuilder:validation:Enum=Password;UUID -type GeneratorKind string - -const ( - GeneratorKindPassword GeneratorKind = "Password" - GeneratorKindUUID GeneratorKind = "UUID" -) - -type ClusterGeneratorSpec struct { - // Kind the kind of this generator. - Kind GeneratorKind `json:"kind"` - - // Generator the spec for this generator, must match the kind. - Generator GeneratorSpec `json:"generator,omitempty"` -} - -type GeneratorSpec struct { - // +kubebuilder:validation:Optional - PasswordSpec *PasswordSpec `json:"passwordSpec,omitempty"` - // +kubebuilder:validation:Optional - UUIDSpec *UUIDSpec `json:"uuidSpec,omitempty"` -} - -// ClusterGenerator represents a cluster-wide generator -// +kubebuilder:object:root=true -// +kubebuilder:storageversion -// +kubebuilder:subresource:status -// +kubebuilder:resource:scope=Cluster -type ClusterGenerator struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec ClusterGeneratorSpec `json:"spec,omitempty"` -} - -// +kubebuilder:object:root=true - -// ClusterGeneratorList contains a list of ClusterGenerator resources. -type ClusterGeneratorList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []ClusterGenerator `json:"items"` -} - -// ! UUID Generator - -// UUIDSpec controls the behavior of the uuid generator. -type UUIDSpec struct{} - -// UUID generates a version 4 UUID (e56657e3-764f-11ef-a397-65231a88c216). -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status -type UUID struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec UUIDSpec `json:"spec,omitempty"` -} - -// +kubebuilder:object:root=true - -// UUIDList contains a list of UUID resources. -type UUIDList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []UUID `json:"items"` -} - -// ! Password Generator - -// PasswordSpec controls the behavior of the password generator. -type PasswordSpec struct { - // Length of the password to be generated. - // Defaults to 24 - // +kubebuilder:validation:Optional - // +kubebuilder:default=24 - Length int `json:"length"` - - // digits specifies the number of digits in the generated - // password. If omitted it defaults to 25% of the length of the password - Digits *int `json:"digits,omitempty"` - - // symbols specifies the number of symbol characters in the generated - // password. If omitted it defaults to 25% of the length of the password - Symbols *int `json:"symbols,omitempty"` - - // symbolCharacters specifies the special characters that should be used - // in the generated password. - SymbolCharacters *string `json:"symbolCharacters,omitempty"` - - // Set noUpper to disable uppercase characters - // +kubebuilder:validation:Optional - // +kubebuilder:default=false - NoUpper bool `json:"noUpper"` - - // set allowRepeat to true to allow repeating characters. - // +kubebuilder:validation:Optional - // +kubebuilder:default=false - AllowRepeat bool `json:"allowRepeat"` -} - -// Password generates a random password based on the -// configuration parameters in spec. -// You can specify the length, characterset and other attributes. -// +kubebuilder:object:root=true -// +kubebuilder:storageversion -// +kubebuilder:subresource:status -// +kubebuilder:resource:scope=Namespaced -type Password struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec PasswordSpec `json:"spec,omitempty"` -} - -// +kubebuilder:object:root=true - -// PasswordList contains a list of Password resources. -type PasswordList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []Password `json:"items"` -} - -func init() { - SchemeBuilder.Register(&Password{}, &PasswordList{}) - SchemeBuilder.Register(&UUID{}, &UUIDList{}) - SchemeBuilder.Register(&ClusterGenerator{}, &ClusterGeneratorList{}) -} diff --git a/k8-operator/k8-operator/api/v1alpha1/groupversion_info.go b/k8-operator/k8-operator/api/v1alpha1/groupversion_info.go deleted file mode 100644 index 36ebd80ce..000000000 --- a/k8-operator/k8-operator/api/v1alpha1/groupversion_info.go +++ /dev/null @@ -1,20 +0,0 @@ -// Package v1alpha1 contains API Schema definitions for the secrets v1alpha1 API group -// +kubebuilder:object:generate=true -// +groupName=secrets.infisical.com -package v1alpha1 - -import ( - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/scheme" -) - -var ( - // GroupVersion is group version used to register these objects - GroupVersion = schema.GroupVersion{Group: "secrets.infisical.com", Version: "v1alpha1"} - - // SchemeBuilder is used to add go types to the GroupVersionKind scheme - SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} - - // AddToScheme adds the types in this group-version to the given scheme. - AddToScheme = SchemeBuilder.AddToScheme -) diff --git a/k8-operator/k8-operator/api/v1alpha1/infisicaldynamicsecret_types.go b/k8-operator/k8-operator/api/v1alpha1/infisicaldynamicsecret_types.go deleted file mode 100644 index a55e215a3..000000000 --- a/k8-operator/k8-operator/api/v1alpha1/infisicaldynamicsecret_types.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Copyright 2022. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type InfisicalDynamicSecretLease struct { - ID string `json:"id"` - Version int64 `json:"version"` - CreationTimestamp metav1.Time `json:"creationTimestamp"` - ExpiresAt metav1.Time `json:"expiresAt"` -} - -type DynamicSecretDetails struct { - // +kubebuilder:validation:Required - // +kubebuilder:validation:Immutable - SecretName string `json:"secretName"` - // +kubebuilder:validation:Required - // +kubebuilder:validation:Immutable - SecretPath string `json:"secretsPath"` - // +kubebuilder:validation:Required - // +kubebuilder:validation:Immutable - EnvironmentSlug string `json:"environmentSlug"` - // +kubebuilder:validation:Required - // +kubebuilder:validation:Immutable - ProjectID string `json:"projectId"` -} - -// InfisicalDynamicSecretSpec defines the desired state of InfisicalDynamicSecret. -type InfisicalDynamicSecretSpec struct { - // +kubebuilder:validation:Required - ManagedSecretReference ManagedKubeSecretConfig `json:"managedSecretReference"` // The destination to store the lease in. - - // +kubebuilder:validation:Required - Authentication GenericInfisicalAuthentication `json:"authentication"` // The authentication to use for authenticating with Infisical. - - // +kubebuilder:validation:Required - DynamicSecret DynamicSecretDetails `json:"dynamicSecret"` // The dynamic secret to create the lease for. Required. - - LeaseRevocationPolicy string `json:"leaseRevocationPolicy"` // Revoke will revoke the lease when the resource is deleted. Optional, will default to no revocation. - LeaseTTL string `json:"leaseTTL"` // The TTL of the lease in seconds. Optional, will default to the dynamic secret default TTL. - - // +kubebuilder:validation:Optional - HostAPI string `json:"hostAPI"` - - // +kubebuilder:validation:Optional - TLS TLSConfig `json:"tls"` -} - -// InfisicalDynamicSecretStatus defines the observed state of InfisicalDynamicSecret. -type InfisicalDynamicSecretStatus struct { - Conditions []metav1.Condition `json:"conditions"` - - Lease *InfisicalDynamicSecretLease `json:"lease,omitempty"` - DynamicSecretID string `json:"dynamicSecretId,omitempty"` - // The MaxTTL can be null, if it's null, there's no max TTL and we should never have to renew. - MaxTTL string `json:"maxTTL,omitempty"` -} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status - -// InfisicalDynamicSecret is the Schema for the infisicaldynamicsecrets API. -type InfisicalDynamicSecret struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec InfisicalDynamicSecretSpec `json:"spec,omitempty"` - Status InfisicalDynamicSecretStatus `json:"status,omitempty"` -} - -// +kubebuilder:object:root=true - -// InfisicalDynamicSecretList contains a list of InfisicalDynamicSecret. -type InfisicalDynamicSecretList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []InfisicalDynamicSecret `json:"items"` -} - -func init() { - SchemeBuilder.Register(&InfisicalDynamicSecret{}, &InfisicalDynamicSecretList{}) -} diff --git a/k8-operator/k8-operator/api/v1alpha1/infisicalpushsecret_types.go b/k8-operator/k8-operator/api/v1alpha1/infisicalpushsecret_types.go deleted file mode 100644 index 8958c714d..000000000 --- a/k8-operator/k8-operator/api/v1alpha1/infisicalpushsecret_types.go +++ /dev/null @@ -1,115 +0,0 @@ -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type InfisicalPushSecretDestination struct { - // +kubebuilder:validation:Required - // +kubebuilder:validation:Immutable - SecretsPath string `json:"secretsPath"` - // +kubebuilder:validation:Required - // +kubebuilder:validation:Immutable - EnvironmentSlug string `json:"environmentSlug"` - // +kubebuilder:validation:Required - // +kubebuilder:validation:Immutable - ProjectID string `json:"projectId"` -} - -type InfisicalPushSecretSecretSource struct { - // The name of the Kubernetes Secret - // +kubebuilder:validation:Required - SecretName string `json:"secretName"` - - // The name space where the Kubernetes Secret is located - // +kubebuilder:validation:Required - SecretNamespace string `json:"secretNamespace"` - - // +kubebuilder:validation:Optional - Template *SecretTemplate `json:"template,omitempty"` -} - -type GeneratorRef struct { - // Specify the Kind of the generator resource - // +kubebuilder:validation:Enum=Password;UUID - // +kubebuilder:validation:Required - Kind GeneratorKind `json:"kind"` - - // +kubebuilder:validation:Required - Name string `json:"name"` -} - -type SecretPushGenerator struct { - // +kubebuilder:validation:Required - DestinationSecretName string `json:"destinationSecretName"` - // +kubebuilder:validation:Required - GeneratorRef GeneratorRef `json:"generatorRef"` -} - -type SecretPush struct { - // +kubebuilder:validation:Optional - Secret *InfisicalPushSecretSecretSource `json:"secret,omitempty"` - // +kubebuilder:validation:Optional - Generators []SecretPushGenerator `json:"generators,omitempty"` -} - -// InfisicalPushSecretSpec defines the desired state of InfisicalPushSecret -type InfisicalPushSecretSpec struct { - // +kubebuilder:validation:Optional - UpdatePolicy string `json:"updatePolicy"` - - // +kubebuilder:validation:Optional - DeletionPolicy string `json:"deletionPolicy"` - - // +kubebuilder:validation:Required - // +kubebuilder:validation:Immutable - Destination InfisicalPushSecretDestination `json:"destination"` - - // +kubebuilder:validation:Optional - Authentication GenericInfisicalAuthentication `json:"authentication"` - - // +kubebuilder:validation:Required - Push SecretPush `json:"push"` - - // +kubebuilder:validation:Optional - ResyncInterval *string `json:"resyncInterval,omitempty"` - - // Infisical host to pull secrets from - // +kubebuilder:validation:Optional - HostAPI string `json:"hostAPI"` - - // +kubebuilder:validation:Optional - TLS TLSConfig `json:"tls"` -} - -// InfisicalPushSecretStatus defines the observed state of InfisicalPushSecret -type InfisicalPushSecretStatus struct { - Conditions []metav1.Condition `json:"conditions"` - - // managed secrets is a map where the key is the ID, and the value is the secret key (string[id], string[key] ) - ManagedSecrets map[string]string `json:"managedSecrets"` -} - -// +kubebuilder:object:root=true -// +kubebuilder:subresource:status -// InfisicalPushSecret is the Schema for the infisicalpushsecrets API -type InfisicalPushSecret struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec InfisicalPushSecretSpec `json:"spec,omitempty"` - Status InfisicalPushSecretStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// InfisicalPushSecretList contains a list of InfisicalPushSecret -type InfisicalPushSecretList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []InfisicalPushSecret `json:"items"` -} - -func init() { - SchemeBuilder.Register(&InfisicalPushSecret{}, &InfisicalPushSecretList{}) -} diff --git a/k8-operator/k8-operator/api/v1alpha1/infisicalsecret_types.go b/k8-operator/k8-operator/api/v1alpha1/infisicalsecret_types.go deleted file mode 100644 index ff26a878c..000000000 --- a/k8-operator/k8-operator/api/v1alpha1/infisicalsecret_types.go +++ /dev/null @@ -1,182 +0,0 @@ -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -type Authentication struct { - // +kubebuilder:validation:Optional - ServiceAccount ServiceAccountDetails `json:"serviceAccount"` - // +kubebuilder:validation:Optional - ServiceToken ServiceTokenDetails `json:"serviceToken"` - // +kubebuilder:validation:Optional - UniversalAuth UniversalAuthDetails `json:"universalAuth"` - // +kubebuilder:validation:Optional - KubernetesAuth KubernetesAuthDetails `json:"kubernetesAuth"` - // +kubebuilder:validation:Optional - AwsIamAuth AWSIamAuthDetails `json:"awsIamAuth"` - // +kubebuilder:validation:Optional - AzureAuth AzureAuthDetails `json:"azureAuth"` - // +kubebuilder:validation:Optional - GcpIdTokenAuth GCPIdTokenAuthDetails `json:"gcpIdTokenAuth"` - // +kubebuilder:validation:Optional - GcpIamAuth GcpIamAuthDetails `json:"gcpIamAuth"` -} - -type UniversalAuthDetails struct { - // +kubebuilder:validation:Required - CredentialsRef KubeSecretReference `json:"credentialsRef"` - // +kubebuilder:validation:Required - SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"` -} - -type KubernetesAuthDetails struct { - // +kubebuilder:validation:Required - IdentityID string `json:"identityId"` - // +kubebuilder:validation:Required - ServiceAccountRef KubernetesServiceAccountRef `json:"serviceAccountRef"` - - // +kubebuilder:validation:Required - SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"` - - // Optionally automatically create a service account token for the configured service account. - // If this is set to `true`, the operator will automatically create a service account token for the configured service account. - // +kubebuilder:validation:Optional - AutoCreateServiceAccountToken bool `json:"autoCreateServiceAccountToken"` - // The audiences to use for the service account token. This is only relevant if `autoCreateServiceAccountToken` is true. - // +kubebuilder:validation:Optional - ServiceAccountTokenAudiences []string `json:"serviceAccountTokenAudiences"` -} - -type KubernetesServiceAccountRef struct { - // +kubebuilder:validation:Required - Name string `json:"name"` - // +kubebuilder:validation:Required - Namespace string `json:"namespace"` -} - -type AWSIamAuthDetails struct { - // +kubebuilder:validation:Required - IdentityID string `json:"identityId"` - - // +kubebuilder:validation:Required - SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"` -} - -type AzureAuthDetails struct { - // +kubebuilder:validation:Required - IdentityID string `json:"identityId"` - // +kubebuilder:validation:Optional - Resource string `json:"resource"` - - // +kubebuilder:validation:Required - SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"` -} - -type GCPIdTokenAuthDetails struct { - // +kubebuilder:validation:Required - IdentityID string `json:"identityId"` - - // +kubebuilder:validation:Required - SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"` -} - -type GcpIamAuthDetails struct { - // +kubebuilder:validation:Required - IdentityID string `json:"identityId"` - // +kubebuilder:validation:Required - ServiceAccountKeyFilePath string `json:"serviceAccountKeyFilePath"` - - // +kubebuilder:validation:Required - SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"` -} - -type ServiceTokenDetails struct { - // +kubebuilder:validation:Required - ServiceTokenSecretReference KubeSecretReference `json:"serviceTokenSecretReference"` - // +kubebuilder:validation:Required - SecretsScope SecretScopeInWorkspace `json:"secretsScope"` -} - -type ServiceAccountDetails struct { - ServiceAccountSecretReference KubeSecretReference `json:"serviceAccountSecretReference"` - ProjectId string `json:"projectId"` - EnvironmentName string `json:"environmentName"` -} - -type SecretScopeInWorkspace struct { - // +kubebuilder:validation:Required - SecretsPath string `json:"secretsPath"` - // +kubebuilder:validation:Required - EnvSlug string `json:"envSlug"` - // +kubebuilder:validation:Optional - Recursive bool `json:"recursive"` -} - -type MachineIdentityScopeInWorkspace struct { - // +kubebuilder:validation:Required - SecretsPath string `json:"secretsPath"` - // +kubebuilder:validation:Required - EnvSlug string `json:"envSlug"` - // +kubebuilder:validation:Required - ProjectSlug string `json:"projectSlug"` - // +kubebuilder:validation:Optional - Recursive bool `json:"recursive"` -} - -// InfisicalSecretSpec defines the desired state of InfisicalSecret -type InfisicalSecretSpec struct { - // +kubebuilder:validation:Optional - TokenSecretReference KubeSecretReference `json:"tokenSecretReference"` - - // +kubebuilder:validation:Optional - Authentication Authentication `json:"authentication"` - - // +kubebuilder:validation:Optional - ManagedSecretReference ManagedKubeSecretConfig `json:"managedSecretReference"` - - // +kubebuilder:validation:Optional - ManagedKubeSecretReferences []ManagedKubeSecretConfig `json:"managedKubeSecretReferences"` - // +kubebuilder:validation:Optional - ManagedKubeConfigMapReferences []ManagedKubeConfigMapConfig `json:"managedKubeConfigMapReferences"` - - // +kubebuilder:default:=60 - ResyncInterval int `json:"resyncInterval"` - - // Infisical host to pull secrets from - // +kubebuilder:validation:Optional - HostAPI string `json:"hostAPI"` - - // +kubebuilder:validation:Optional - TLS TLSConfig `json:"tls"` -} - -// InfisicalSecretStatus defines the observed state of InfisicalSecret -type InfisicalSecretStatus struct { - Conditions []metav1.Condition `json:"conditions"` -} - -//+kubebuilder:object:root=true -//+kubebuilder:subresource:status - -// InfisicalSecret is the Schema for the infisicalsecrets API -type InfisicalSecret struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec InfisicalSecretSpec `json:"spec,omitempty"` - Status InfisicalSecretStatus `json:"status,omitempty"` -} - -//+kubebuilder:object:root=true - -// InfisicalSecretList contains a list of InfisicalSecret -type InfisicalSecretList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata,omitempty"` - Items []InfisicalSecret `json:"items"` -} - -func init() { - SchemeBuilder.Register(&InfisicalSecret{}, &InfisicalSecretList{}) -} diff --git a/k8-operator/k8-operator/api/v1alpha1/zz_generated.deepcopy.go b/k8-operator/k8-operator/api/v1alpha1/zz_generated.deepcopy.go deleted file mode 100644 index cc4d39c19..000000000 --- a/k8-operator/k8-operator/api/v1alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,307 +0,0 @@ -//go:build !ignore_autogenerated - -/* -Copyright 2025. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by controller-gen. DO NOT EDIT. - -package v1alpha1 - -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 *InfisicalDynamicSecret) DeepCopyInto(out *InfisicalDynamicSecret) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecret. -func (in *InfisicalDynamicSecret) DeepCopy() *InfisicalDynamicSecret { - if in == nil { - return nil - } - out := new(InfisicalDynamicSecret) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *InfisicalDynamicSecret) 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 *InfisicalDynamicSecretList) DeepCopyInto(out *InfisicalDynamicSecretList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]InfisicalDynamicSecret, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecretList. -func (in *InfisicalDynamicSecretList) DeepCopy() *InfisicalDynamicSecretList { - if in == nil { - return nil - } - out := new(InfisicalDynamicSecretList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *InfisicalDynamicSecretList) 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 *InfisicalDynamicSecretSpec) DeepCopyInto(out *InfisicalDynamicSecretSpec) { - *out = *in - if in.Foo != nil { - in, out := &in.Foo, &out.Foo - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecretSpec. -func (in *InfisicalDynamicSecretSpec) DeepCopy() *InfisicalDynamicSecretSpec { - if in == nil { - return nil - } - out := new(InfisicalDynamicSecretSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *InfisicalDynamicSecretStatus) DeepCopyInto(out *InfisicalDynamicSecretStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalDynamicSecretStatus. -func (in *InfisicalDynamicSecretStatus) DeepCopy() *InfisicalDynamicSecretStatus { - if in == nil { - return nil - } - out := new(InfisicalDynamicSecretStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *InfisicalPushSecretSecret) DeepCopyInto(out *InfisicalPushSecretSecret) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalPushSecretSecret. -func (in *InfisicalPushSecretSecret) DeepCopy() *InfisicalPushSecretSecret { - if in == nil { - return nil - } - out := new(InfisicalPushSecretSecret) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *InfisicalPushSecretSecret) 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 *InfisicalPushSecretSecretList) DeepCopyInto(out *InfisicalPushSecretSecretList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]InfisicalPushSecretSecret, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalPushSecretSecretList. -func (in *InfisicalPushSecretSecretList) DeepCopy() *InfisicalPushSecretSecretList { - if in == nil { - return nil - } - out := new(InfisicalPushSecretSecretList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *InfisicalPushSecretSecretList) 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 *InfisicalPushSecretSecretSpec) DeepCopyInto(out *InfisicalPushSecretSecretSpec) { - *out = *in - if in.Foo != nil { - in, out := &in.Foo, &out.Foo - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalPushSecretSecretSpec. -func (in *InfisicalPushSecretSecretSpec) DeepCopy() *InfisicalPushSecretSecretSpec { - if in == nil { - return nil - } - out := new(InfisicalPushSecretSecretSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *InfisicalPushSecretSecretStatus) DeepCopyInto(out *InfisicalPushSecretSecretStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalPushSecretSecretStatus. -func (in *InfisicalPushSecretSecretStatus) DeepCopy() *InfisicalPushSecretSecretStatus { - if in == nil { - return nil - } - out := new(InfisicalPushSecretSecretStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *InfisicalSecret) DeepCopyInto(out *InfisicalSecret) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - out.Status = in.Status -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalSecret. -func (in *InfisicalSecret) DeepCopy() *InfisicalSecret { - if in == nil { - return nil - } - out := new(InfisicalSecret) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *InfisicalSecret) 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 *InfisicalSecretList) DeepCopyInto(out *InfisicalSecretList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]InfisicalSecret, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalSecretList. -func (in *InfisicalSecretList) DeepCopy() *InfisicalSecretList { - if in == nil { - return nil - } - out := new(InfisicalSecretList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *InfisicalSecretList) 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 *InfisicalSecretSpec) DeepCopyInto(out *InfisicalSecretSpec) { - *out = *in - if in.Foo != nil { - in, out := &in.Foo, &out.Foo - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalSecretSpec. -func (in *InfisicalSecretSpec) DeepCopy() *InfisicalSecretSpec { - if in == nil { - return nil - } - out := new(InfisicalSecretSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *InfisicalSecretStatus) DeepCopyInto(out *InfisicalSecretStatus) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalSecretStatus. -func (in *InfisicalSecretStatus) DeepCopy() *InfisicalSecretStatus { - if in == nil { - return nil - } - out := new(InfisicalSecretStatus) - in.DeepCopyInto(out) - return out -} diff --git a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_clustergenerators.yaml b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_clustergenerators.yaml deleted file mode 100644 index 0681f26ec..000000000 --- a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_clustergenerators.yaml +++ /dev/null @@ -1,96 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.18.0 - name: clustergenerators.secrets.infisical.com -spec: - group: secrets.infisical.com - names: - kind: ClusterGenerator - listKind: ClusterGeneratorList - plural: clustergenerators - singular: clustergenerator - scope: Cluster - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: ClusterGenerator represents a cluster-wide generator - 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: - properties: - generator: - description: Generator the spec for this generator, must match the - kind. - properties: - passwordSpec: - description: PasswordSpec controls the behavior of the password - generator. - properties: - allowRepeat: - default: false - description: set allowRepeat to true to allow repeating characters. - type: boolean - digits: - description: |- - digits specifies the number of digits in the generated - password. If omitted it defaults to 25% of the length of the password - type: integer - length: - default: 24 - description: |- - Length of the password to be generated. - Defaults to 24 - type: integer - noUpper: - default: false - description: Set noUpper to disable uppercase characters - type: boolean - symbolCharacters: - description: |- - symbolCharacters specifies the special characters that should be used - in the generated password. - type: string - symbols: - description: |- - symbols specifies the number of symbol characters in the generated - password. If omitted it defaults to 25% of the length of the password - type: integer - type: object - uuidSpec: - description: UUIDSpec controls the behavior of the uuid generator. - type: object - type: object - kind: - description: Kind the kind of this generator. - enum: - - Password - - UUID - type: string - required: - - kind - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicaldynamicsecrets.yaml b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicaldynamicsecrets.yaml deleted file mode 100644 index a71c90fd0..000000000 --- a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicaldynamicsecrets.yaml +++ /dev/null @@ -1,309 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.18.0 - name: infisicaldynamicsecrets.secrets.infisical.com -spec: - group: secrets.infisical.com - names: - kind: InfisicalDynamicSecret - listKind: InfisicalDynamicSecretList - plural: infisicaldynamicsecrets - singular: infisicaldynamicsecret - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: InfisicalDynamicSecret is the Schema for the infisicaldynamicsecrets - 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: InfisicalDynamicSecretSpec defines the desired state of InfisicalDynamicSecret. - properties: - authentication: - properties: - awsIamAuth: - properties: - identityId: - type: string - required: - - identityId - type: object - azureAuth: - properties: - identityId: - type: string - resource: - type: string - required: - - identityId - type: object - gcpIamAuth: - properties: - identityId: - type: string - serviceAccountKeyFilePath: - type: string - required: - - identityId - - serviceAccountKeyFilePath - type: object - gcpIdTokenAuth: - properties: - identityId: - type: string - required: - - identityId - type: object - kubernetesAuth: - properties: - autoCreateServiceAccountToken: - description: |- - Optionally automatically create a service account token for the configured service account. - If this is set to `true`, the operator will automatically create a service account token for the configured service account. This field is recommended in most cases. - type: boolean - identityId: - type: string - serviceAccountRef: - properties: - name: - type: string - namespace: - type: string - required: - - name - - namespace - type: object - serviceAccountTokenAudiences: - description: The audiences to use for the service account - token. This is only relevant if `autoCreateServiceAccountToken` - is true. - items: - type: string - type: array - required: - - identityId - - serviceAccountRef - type: object - universalAuth: - properties: - credentialsRef: - properties: - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret - is located - type: string - required: - - secretName - - secretNamespace - type: object - required: - - credentialsRef - type: object - type: object - dynamicSecret: - properties: - environmentSlug: - type: string - projectId: - type: string - secretName: - type: string - secretsPath: - type: string - required: - - environmentSlug - - projectId - - secretName - - secretsPath - type: object - hostAPI: - type: string - leaseRevocationPolicy: - type: string - leaseTTL: - type: string - managedSecretReference: - properties: - creationPolicy: - default: Orphan - description: |- - The Kubernetes Secret creation policy. - Enum with values: 'Owner', 'Orphan'. - Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. - Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted. - type: string - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret is located - type: string - secretType: - default: Opaque - description: 'The Kubernetes Secret type (experimental feature). - More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types' - type: string - template: - description: The template to transform the secret data - properties: - data: - additionalProperties: - type: string - description: The template key values - type: object - includeAllSecrets: - description: |- - This injects all retrieved secrets into the top level of your template. - Secrets defined in the template will take precedence over the injected ones. - type: boolean - type: object - required: - - secretName - - secretNamespace - type: object - tls: - properties: - caRef: - description: Reference to secret containing CA cert - properties: - key: - description: The name of the secret property with the CA certificate - value - type: string - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The namespace where the Kubernetes Secret is - located - type: string - required: - - key - - secretName - - secretNamespace - type: object - type: object - required: - - authentication - - dynamicSecret - - leaseRevocationPolicy - - leaseTTL - - managedSecretReference - type: object - status: - description: InfisicalDynamicSecretStatus defines the observed state of - InfisicalDynamicSecret. - properties: - conditions: - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - dynamicSecretId: - type: string - lease: - properties: - creationTimestamp: - format: date-time - type: string - expiresAt: - format: date-time - type: string - id: - type: string - version: - format: int64 - type: integer - required: - - creationTimestamp - - expiresAt - - id - - version - type: object - maxTTL: - description: The MaxTTL can be null, if it's null, there's no max - TTL and we should never have to renew. - type: string - required: - - conditions - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml deleted file mode 100644 index beed2fd50..000000000 --- a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecrets.yaml +++ /dev/null @@ -1,305 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.18.0 - name: infisicalpushsecrets.secrets.infisical.com -spec: - group: secrets.infisical.com - names: - kind: InfisicalPushSecret - listKind: InfisicalPushSecretList - plural: infisicalpushsecrets - singular: infisicalpushsecret - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: InfisicalPushSecret is the Schema for the infisicalpushsecrets - 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: InfisicalPushSecretSpec defines the desired state of InfisicalPushSecret - properties: - authentication: - properties: - awsIamAuth: - properties: - identityId: - type: string - required: - - identityId - type: object - azureAuth: - properties: - identityId: - type: string - resource: - type: string - required: - - identityId - type: object - gcpIamAuth: - properties: - identityId: - type: string - serviceAccountKeyFilePath: - type: string - required: - - identityId - - serviceAccountKeyFilePath - type: object - gcpIdTokenAuth: - properties: - identityId: - type: string - required: - - identityId - type: object - kubernetesAuth: - properties: - autoCreateServiceAccountToken: - description: |- - Optionally automatically create a service account token for the configured service account. - If this is set to `true`, the operator will automatically create a service account token for the configured service account. This field is recommended in most cases. - type: boolean - identityId: - type: string - serviceAccountRef: - properties: - name: - type: string - namespace: - type: string - required: - - name - - namespace - type: object - serviceAccountTokenAudiences: - description: The audiences to use for the service account - token. This is only relevant if `autoCreateServiceAccountToken` - is true. - items: - type: string - type: array - required: - - identityId - - serviceAccountRef - type: object - universalAuth: - properties: - credentialsRef: - properties: - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret - is located - type: string - required: - - secretName - - secretNamespace - type: object - required: - - credentialsRef - type: object - type: object - deletionPolicy: - type: string - destination: - properties: - environmentSlug: - type: string - projectId: - type: string - secretsPath: - type: string - required: - - environmentSlug - - projectId - - secretsPath - type: object - hostAPI: - description: Infisical host to pull secrets from - type: string - push: - properties: - generators: - items: - properties: - destinationSecretName: - type: string - generatorRef: - properties: - kind: - allOf: - - enum: - - Password - - UUID - - enum: - - Password - - UUID - description: Specify the Kind of the generator resource - type: string - name: - type: string - required: - - kind - - name - type: object - required: - - destinationSecretName - - generatorRef - type: object - type: array - secret: - properties: - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret is - located - type: string - template: - properties: - data: - additionalProperties: - type: string - description: The template key values - type: object - includeAllSecrets: - description: |- - This injects all retrieved secrets into the top level of your template. - Secrets defined in the template will take precedence over the injected ones. - type: boolean - type: object - required: - - secretName - - secretNamespace - type: object - type: object - resyncInterval: - type: string - tls: - properties: - caRef: - description: Reference to secret containing CA cert - properties: - key: - description: The name of the secret property with the CA certificate - value - type: string - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The namespace where the Kubernetes Secret is - located - type: string - required: - - key - - secretName - - secretNamespace - type: object - type: object - updatePolicy: - type: string - required: - - destination - - push - type: object - status: - description: InfisicalPushSecretStatus defines the observed state of InfisicalPushSecret - properties: - conditions: - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - managedSecrets: - additionalProperties: - type: string - description: managed secrets is a map where the key is the ID, and - the value is the secret key (string[id], string[key] ) - type: object - required: - - conditions - - managedSecrets - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecretsecrets.yaml b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecretsecrets.yaml deleted file mode 100644 index e94900eb2..000000000 --- a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalpushsecretsecrets.yaml +++ /dev/null @@ -1,57 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.18.0 - name: infisicalpushsecretsecrets.secrets.infisical.com -spec: - group: secrets.infisical.com - names: - kind: InfisicalPushSecretSecret - listKind: InfisicalPushSecretSecretList - plural: infisicalpushsecretsecrets - singular: infisicalpushsecretsecret - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: InfisicalPushSecretSecret is the Schema for the infisicalpushsecretsecrets - 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: spec defines the desired state of InfisicalPushSecretSecret - properties: - foo: - description: foo is an example field of InfisicalPushSecretSecret. - Edit infisicalpushsecretsecret_types.go to remove/update - type: string - type: object - status: - description: status defines the observed state of InfisicalPushSecretSecret - type: object - required: - - spec - type: object - served: true - storage: true - subresources: - status: {} diff --git a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml deleted file mode 100644 index c1bc91f59..000000000 --- a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml +++ /dev/null @@ -1,503 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.18.0 - name: infisicalsecrets.secrets.infisical.com -spec: - group: secrets.infisical.com - names: - kind: InfisicalSecret - listKind: InfisicalSecretList - plural: infisicalsecrets - singular: infisicalsecret - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: InfisicalSecret is the Schema for the infisicalsecrets 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: InfisicalSecretSpec defines the desired state of InfisicalSecret - properties: - authentication: - properties: - awsIamAuth: - properties: - identityId: - type: string - secretsScope: - properties: - envSlug: - type: string - projectSlug: - type: string - recursive: - type: boolean - secretsPath: - type: string - required: - - envSlug - - projectSlug - - secretsPath - type: object - required: - - identityId - - secretsScope - type: object - azureAuth: - properties: - identityId: - type: string - resource: - type: string - secretsScope: - properties: - envSlug: - type: string - projectSlug: - type: string - recursive: - type: boolean - secretsPath: - type: string - required: - - envSlug - - projectSlug - - secretsPath - type: object - required: - - identityId - - secretsScope - type: object - gcpIamAuth: - properties: - identityId: - type: string - secretsScope: - properties: - envSlug: - type: string - projectSlug: - type: string - recursive: - type: boolean - secretsPath: - type: string - required: - - envSlug - - projectSlug - - secretsPath - type: object - serviceAccountKeyFilePath: - type: string - required: - - identityId - - secretsScope - - serviceAccountKeyFilePath - type: object - gcpIdTokenAuth: - properties: - identityId: - type: string - secretsScope: - properties: - envSlug: - type: string - projectSlug: - type: string - recursive: - type: boolean - secretsPath: - type: string - required: - - envSlug - - projectSlug - - secretsPath - type: object - required: - - identityId - - secretsScope - type: object - kubernetesAuth: - properties: - autoCreateServiceAccountToken: - description: |- - Optionally automatically create a service account token for the configured service account. - If this is set to `true`, the operator will automatically create a service account token for the configured service account. - type: boolean - identityId: - type: string - secretsScope: - properties: - envSlug: - type: string - projectSlug: - type: string - recursive: - type: boolean - secretsPath: - type: string - required: - - envSlug - - projectSlug - - secretsPath - type: object - serviceAccountRef: - properties: - name: - type: string - namespace: - type: string - required: - - name - - namespace - type: object - serviceAccountTokenAudiences: - description: The audiences to use for the service account - token. This is only relevant if `autoCreateServiceAccountToken` - is true. - items: - type: string - type: array - required: - - identityId - - secretsScope - - serviceAccountRef - type: object - serviceAccount: - properties: - environmentName: - type: string - projectId: - type: string - serviceAccountSecretReference: - properties: - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret - is located - type: string - required: - - secretName - - secretNamespace - type: object - required: - - environmentName - - projectId - - serviceAccountSecretReference - type: object - serviceToken: - properties: - secretsScope: - properties: - envSlug: - type: string - recursive: - type: boolean - secretsPath: - type: string - required: - - envSlug - - secretsPath - type: object - serviceTokenSecretReference: - properties: - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret - is located - type: string - required: - - secretName - - secretNamespace - type: object - required: - - secretsScope - - serviceTokenSecretReference - type: object - universalAuth: - properties: - credentialsRef: - properties: - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret - is located - type: string - required: - - secretName - - secretNamespace - type: object - secretsScope: - properties: - envSlug: - type: string - projectSlug: - type: string - recursive: - type: boolean - secretsPath: - type: string - required: - - envSlug - - projectSlug - - secretsPath - type: object - required: - - credentialsRef - - secretsScope - type: object - type: object - hostAPI: - description: Infisical host to pull secrets from - type: string - managedKubeConfigMapReferences: - items: - properties: - configMapName: - description: The name of the Kubernetes ConfigMap - type: string - configMapNamespace: - description: The namespace where the Kubernetes ConfigMap is - located - type: string - creationPolicy: - default: Orphan - description: |- - The Kubernetes ConfigMap creation policy. - Enum with values: 'Owner', 'Orphan'. - Owner creates the config map and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. - Orphan will not set the config map owner. This will result in the config map being orphaned and not deleted when the resource is deleted. - type: string - template: - description: The template to transform the secret data - properties: - data: - additionalProperties: - type: string - description: The template key values - type: object - includeAllSecrets: - description: |- - This injects all retrieved secrets into the top level of your template. - Secrets defined in the template will take precedence over the injected ones. - type: boolean - type: object - required: - - configMapName - - configMapNamespace - type: object - type: array - managedKubeSecretReferences: - items: - properties: - creationPolicy: - default: Orphan - description: |- - The Kubernetes Secret creation policy. - Enum with values: 'Owner', 'Orphan'. - Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. - Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted. - type: string - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret is located - type: string - secretType: - default: Opaque - description: 'The Kubernetes Secret type (experimental feature). - More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types' - type: string - template: - description: The template to transform the secret data - properties: - data: - additionalProperties: - type: string - description: The template key values - type: object - includeAllSecrets: - description: |- - This injects all retrieved secrets into the top level of your template. - Secrets defined in the template will take precedence over the injected ones. - type: boolean - type: object - required: - - secretName - - secretNamespace - type: object - type: array - managedSecretReference: - properties: - creationPolicy: - default: Orphan - description: |- - The Kubernetes Secret creation policy. - Enum with values: 'Owner', 'Orphan'. - Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. - Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted. - type: string - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret is located - type: string - secretType: - default: Opaque - description: 'The Kubernetes Secret type (experimental feature). - More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types' - type: string - template: - description: The template to transform the secret data - properties: - data: - additionalProperties: - type: string - description: The template key values - type: object - includeAllSecrets: - description: |- - This injects all retrieved secrets into the top level of your template. - Secrets defined in the template will take precedence over the injected ones. - type: boolean - type: object - required: - - secretName - - secretNamespace - type: object - resyncInterval: - default: 60 - type: integer - tls: - properties: - caRef: - description: Reference to secret containing CA cert - properties: - key: - description: The name of the secret property with the CA certificate - value - type: string - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The namespace where the Kubernetes Secret is - located - type: string - required: - - key - - secretName - - secretNamespace - type: object - type: object - tokenSecretReference: - properties: - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret is located - type: string - required: - - secretName - - secretNamespace - type: object - required: - - resyncInterval - type: object - status: - description: InfisicalSecretStatus defines the observed state of InfisicalSecret - properties: - conditions: - items: - description: Condition contains details for one aspect of the current - state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - required: - - conditions - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_passwords.yaml b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_passwords.yaml deleted file mode 100644 index 788e077a6..000000000 --- a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_passwords.yaml +++ /dev/null @@ -1,79 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.18.0 - name: passwords.secrets.infisical.com -spec: - group: secrets.infisical.com - names: - kind: Password - listKind: PasswordList - plural: passwords - singular: password - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: |- - Password generates a random password based on the - configuration parameters in spec. - You can specify the length, characterset and other attributes. - 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: PasswordSpec controls the behavior of the password generator. - properties: - allowRepeat: - default: false - description: set allowRepeat to true to allow repeating characters. - type: boolean - digits: - description: |- - digits specifies the number of digits in the generated - password. If omitted it defaults to 25% of the length of the password - type: integer - length: - default: 24 - description: |- - Length of the password to be generated. - Defaults to 24 - type: integer - noUpper: - default: false - description: Set noUpper to disable uppercase characters - type: boolean - symbolCharacters: - description: |- - symbolCharacters specifies the special characters that should be used - in the generated password. - type: string - symbols: - description: |- - symbols specifies the number of symbol characters in the generated - password. If omitted it defaults to 25% of the length of the password - type: integer - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_uuids.yaml b/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_uuids.yaml deleted file mode 100644 index 659dfc7ac..000000000 --- a/k8-operator/k8-operator/config/crd/bases/secrets.infisical.com_uuids.yaml +++ /dev/null @@ -1,46 +0,0 @@ ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - controller-gen.kubebuilder.io/version: v0.18.0 - name: uuids.secrets.infisical.com -spec: - group: secrets.infisical.com - names: - kind: UUID - listKind: UUIDList - plural: uuids - singular: uuid - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: UUID generates a version 4 UUID (e56657e3-764f-11ef-a397-65231a88c216). - 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: UUIDSpec controls the behavior of the uuid generator. - type: object - type: object - served: true - storage: true - subresources: - status: {} diff --git a/k8-operator/k8-operator/config/crd/kustomization.yaml b/k8-operator/k8-operator/config/crd/kustomization.yaml deleted file mode 100644 index a3018a690..000000000 --- a/k8-operator/k8-operator/config/crd/kustomization.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# 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/secrets.infisical.com_infisicalsecrets.yaml -- bases/secrets.infisical.com_infisicalpushsecretsecrets.yaml -- bases/secrets.infisical.com_infisicaldynamicsecrets.yaml -# +kubebuilder:scaffold:crdkustomizeresource - -patches: -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. -# patches here are for enabling the conversion webhook for each CRD -# +kubebuilder:scaffold:crdkustomizewebhookpatch - -# [WEBHOOK] To enable webhook, uncomment the following section -# the following config is for teaching kustomize how to do kustomization for CRDs. -#configurations: -#- kustomizeconfig.yaml diff --git a/k8-operator/k8-operator/config/crd/kustomizeconfig.yaml b/k8-operator/k8-operator/config/crd/kustomizeconfig.yaml deleted file mode 100644 index ec5c150a9..000000000 --- a/k8-operator/k8-operator/config/crd/kustomizeconfig.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# 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/k8-operator/k8-operator/config/default/kustomization.yaml b/k8-operator/k8-operator/config/default/kustomization.yaml deleted file mode 100644 index 8eda77014..000000000 --- a/k8-operator/k8-operator/config/default/kustomization.yaml +++ /dev/null @@ -1,234 +0,0 @@ -# Adds namespace to all resources. -namespace: k8-operator-system - -# Value of this field is prepended to the -# names of all resources, e.g. a deployment named -# "wordpress" becomes "alices-wordpress". -# Note that it should also match with the prefix (text before '-') of the namespace -# field above. -namePrefix: k8-operator- - -# Labels to add to all resources and selectors. -#labels: -#- includeSelectors: true -# pairs: -# someName: someValue - -resources: -- ../crd -- ../rbac -- ../manager -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -#- ../webhook -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. -#- ../certmanager -# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. -#- ../prometheus -# [METRICS] Expose the controller manager metrics service. -- metrics_service.yaml -# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy. -# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics. -# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will -# be able to communicate with the Webhook Server. -#- ../network-policy - -# Uncomment the patches line if you enable Metrics -patches: -# [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. -# More info: https://book.kubebuilder.io/reference/metrics -- path: manager_metrics_patch.yaml - target: - kind: Deployment - -# Uncomment the patches line if you enable Metrics and CertManager -# [METRICS-WITH-CERTS] To enable metrics protected with certManager, uncomment the following line. -# This patch will protect the metrics with certManager self-signed certs. -#- path: cert_metrics_manager_patch.yaml -# target: -# kind: Deployment - -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -#- path: manager_webhook_patch.yaml -# target: -# kind: Deployment - -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -# Uncomment the following replacements to add the cert-manager CA injection annotations -#replacements: -# - source: # Uncomment the following block to enable certificates for metrics -# kind: Service -# version: v1 -# name: controller-manager-metrics-service -# fieldPath: metadata.name -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: metrics-certs -# fieldPaths: -# - spec.dnsNames.0 -# - spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 0 -# create: true -# - select: # Uncomment the following to set the Service name for TLS config in Prometheus ServiceMonitor -# kind: ServiceMonitor -# group: monitoring.coreos.com -# version: v1 -# name: controller-manager-metrics-monitor -# fieldPaths: -# - spec.endpoints.0.tlsConfig.serverName -# options: -# delimiter: '.' -# index: 0 -# create: true - -# - source: -# kind: Service -# version: v1 -# name: controller-manager-metrics-service -# fieldPath: metadata.namespace -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: metrics-certs -# fieldPaths: -# - spec.dnsNames.0 -# - spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 1 -# create: true -# - select: # Uncomment the following to set the Service namespace for TLS in Prometheus ServiceMonitor -# kind: ServiceMonitor -# group: monitoring.coreos.com -# version: v1 -# name: controller-manager-metrics-monitor -# fieldPaths: -# - spec.endpoints.0.tlsConfig.serverName -# options: -# delimiter: '.' -# index: 1 -# create: true - -# - source: # Uncomment the following block if you have any webhook -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.name # Name of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 0 -# create: true -# - source: -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.namespace # Namespace of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 1 -# create: true - -# - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation) -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # This name should match the one in certificate.yaml -# fieldPath: .metadata.namespace # Namespace of the certificate CR -# targets: -# - select: -# kind: ValidatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - source: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPath: .metadata.name -# targets: -# - select: -# kind: ValidatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true - -# - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting ) -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPath: .metadata.namespace # Namespace of the certificate CR -# targets: -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - source: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPath: .metadata.name -# targets: -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true - -# - source: # Uncomment the following block if you have a ConversionWebhook (--conversion) -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPath: .metadata.namespace # Namespace of the certificate CR -# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. -# +kubebuilder:scaffold:crdkustomizecainjectionns -# - source: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPath: .metadata.name -# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. -# +kubebuilder:scaffold:crdkustomizecainjectionname diff --git a/k8-operator/k8-operator/config/manager/kustomization.yaml b/k8-operator/k8-operator/config/manager/kustomization.yaml deleted file mode 100644 index 5c5f0b84c..000000000 --- a/k8-operator/k8-operator/config/manager/kustomization.yaml +++ /dev/null @@ -1,2 +0,0 @@ -resources: -- manager.yaml diff --git a/k8-operator/k8-operator/config/manager/manager.yaml b/k8-operator/k8-operator/config/manager/manager.yaml deleted file mode 100644 index eb41eff84..000000000 --- a/k8-operator/k8-operator/config/manager/manager.yaml +++ /dev/null @@ -1,99 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - control-plane: controller-manager - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize - name: system ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system - labels: - control-plane: controller-manager - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize -spec: - selector: - matchLabels: - control-plane: controller-manager - app.kubernetes.io/name: k8-operator - replicas: 1 - template: - metadata: - annotations: - kubectl.kubernetes.io/default-container: manager - labels: - control-plane: controller-manager - app.kubernetes.io/name: k8-operator - spec: - # TODO(user): Uncomment the following code to configure the nodeAffinity expression - # according to the platforms which are supported by your solution. - # It is considered best practice to support multiple architectures. You can - # build your manager image using the makefile target docker-buildx. - # affinity: - # nodeAffinity: - # requiredDuringSchedulingIgnoredDuringExecution: - # nodeSelectorTerms: - # - matchExpressions: - # - key: kubernetes.io/arch - # operator: In - # values: - # - amd64 - # - arm64 - # - ppc64le - # - s390x - # - key: kubernetes.io/os - # operator: In - # values: - # - linux - securityContext: - # Projects are configured by default to adhere to the "restricted" Pod Security Standards. - # This ensures that deployments meet the highest security requirements for Kubernetes. - # For more details, see: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - containers: - - command: - - /manager - args: - - --leader-elect - - --health-probe-bind-address=:8081 - image: controller:latest - name: manager - ports: [] - securityContext: - readOnlyRootFilesystem: true - allowPrivilegeEscalation: false - capabilities: - drop: - - "ALL" - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - # TODO(user): Configure the resources accordingly based on the project requirements. - # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - volumeMounts: [] - volumes: [] - serviceAccountName: controller-manager - terminationGracePeriodSeconds: 10 diff --git a/k8-operator/k8-operator/config/prometheus/kustomization.yaml b/k8-operator/k8-operator/config/prometheus/kustomization.yaml deleted file mode 100644 index fdc5481b1..000000000 --- a/k8-operator/k8-operator/config/prometheus/kustomization.yaml +++ /dev/null @@ -1,11 +0,0 @@ -resources: -- monitor.yaml - -# [PROMETHEUS-WITH-CERTS] The following patch configures the ServiceMonitor in ../prometheus -# to securely reference certificates created and managed by cert-manager. -# Additionally, ensure that you uncomment the [METRICS WITH CERTMANAGER] patch under config/default/kustomization.yaml -# to mount the "metrics-server-cert" secret in the Manager Deployment. -#patches: -# - path: monitor_tls_patch.yaml -# target: -# kind: ServiceMonitor diff --git a/k8-operator/k8-operator/config/prometheus/monitor.yaml b/k8-operator/k8-operator/config/prometheus/monitor.yaml deleted file mode 100644 index abaef6489..000000000 --- a/k8-operator/k8-operator/config/prometheus/monitor.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# Prometheus Monitor Service (Metrics) -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - labels: - control-plane: controller-manager - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize - name: controller-manager-metrics-monitor - namespace: system -spec: - endpoints: - - path: /metrics - port: https # Ensure this is the name of the port that exposes HTTPS metrics - scheme: https - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token - tlsConfig: - # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables - # certificate verification, exposing the system to potential man-in-the-middle attacks. - # For production environments, it is recommended to use cert-manager for automatic TLS certificate management. - # To apply this configuration, enable cert-manager and use the patch located at config/prometheus/servicemonitor_tls_patch.yaml, - # which securely references the certificate from the 'metrics-server-cert' secret. - insecureSkipVerify: true - selector: - matchLabels: - control-plane: controller-manager - app.kubernetes.io/name: k8-operator diff --git a/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_editor_role.yaml b/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_editor_role.yaml deleted file mode 100644 index 4d3ffc985..000000000 --- a/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_editor_role.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# This rule is not used by the project k8-operator itself. -# It is provided to allow the cluster admin to help manage permissions for users. -# -# Grants permissions to create, update, and delete resources within the secrets.infisical.com. -# This role is intended for users who need to manage these resources -# but should not control RBAC or manage permissions for others. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize - name: infisicaldynamicsecret-editor-role -rules: -- apiGroups: - - secrets.infisical.com - resources: - - infisicaldynamicsecrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - secrets.infisical.com - resources: - - infisicaldynamicsecrets/status - verbs: - - get diff --git a/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_viewer_role.yaml b/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_viewer_role.yaml deleted file mode 100644 index d5cfc7be9..000000000 --- a/k8-operator/k8-operator/config/rbac/infisicaldynamicsecret_viewer_role.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# This rule is not used by the project k8-operator itself. -# It is provided to allow the cluster admin to help manage permissions for users. -# -# Grants read-only access to secrets.infisical.com resources. -# This role is intended for users who need visibility into these resources -# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize - name: infisicaldynamicsecret-viewer-role -rules: -- apiGroups: - - secrets.infisical.com - resources: - - infisicaldynamicsecrets - verbs: - - get - - list - - watch -- apiGroups: - - secrets.infisical.com - resources: - - infisicaldynamicsecrets/status - verbs: - - get diff --git a/k8-operator/k8-operator/config/rbac/infisicalsecret_editor_role.yaml b/k8-operator/k8-operator/config/rbac/infisicalsecret_editor_role.yaml deleted file mode 100644 index a70abf3c4..000000000 --- a/k8-operator/k8-operator/config/rbac/infisicalsecret_editor_role.yaml +++ /dev/null @@ -1,33 +0,0 @@ -# This rule is not used by the project k8-operator itself. -# It is provided to allow the cluster admin to help manage permissions for users. -# -# Grants permissions to create, update, and delete resources within the secrets.infisical.com. -# This role is intended for users who need to manage these resources -# but should not control RBAC or manage permissions for others. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize - name: infisicalsecret-editor-role -rules: -- apiGroups: - - secrets.infisical.com - resources: - - infisicalsecrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - secrets.infisical.com - resources: - - infisicalsecrets/status - verbs: - - get diff --git a/k8-operator/k8-operator/config/rbac/infisicalsecret_viewer_role.yaml b/k8-operator/k8-operator/config/rbac/infisicalsecret_viewer_role.yaml deleted file mode 100644 index a5a724940..000000000 --- a/k8-operator/k8-operator/config/rbac/infisicalsecret_viewer_role.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# This rule is not used by the project k8-operator itself. -# It is provided to allow the cluster admin to help manage permissions for users. -# -# Grants read-only access to secrets.infisical.com resources. -# This role is intended for users who need visibility into these resources -# without permissions to modify them. It is ideal for monitoring purposes and limited-access viewing. - -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize - name: infisicalsecret-viewer-role -rules: -- apiGroups: - - secrets.infisical.com - resources: - - infisicalsecrets - verbs: - - get - - list - - watch -- apiGroups: - - secrets.infisical.com - resources: - - infisicalsecrets/status - verbs: - - get diff --git a/k8-operator/k8-operator/config/rbac/kustomization.yaml b/k8-operator/k8-operator/config/rbac/kustomization.yaml deleted file mode 100644 index d879dffa4..000000000 --- a/k8-operator/k8-operator/config/rbac/kustomization.yaml +++ /dev/null @@ -1,34 +0,0 @@ -resources: -# All RBAC will be applied under this service account in -# the deployment namespace. You may comment out this resource -# if your manager will use a service account that exists at -# runtime. Be sure to update RoleBinding and ClusterRoleBinding -# subjects if changing service account names. -- service_account.yaml -- role.yaml -- role_binding.yaml -- leader_election_role.yaml -- leader_election_role_binding.yaml -# The following RBAC configurations are used to protect -# the metrics endpoint with authn/authz. These configurations -# ensure that only authorized users and service accounts -# can access the metrics endpoint. Comment the following -# permissions if you want to disable this protection. -# More info: https://book.kubebuilder.io/reference/metrics.html -- metrics_auth_role.yaml -- metrics_auth_role_binding.yaml -- metrics_reader_role.yaml -# For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by -# default, aiding admins in cluster management. Those roles are -# not used by the k8-operator itself. You can comment the following lines -# if you do not want those helpers be installed with your Project. -- infisicaldynamicsecret_admin_role.yaml -- infisicaldynamicsecret_editor_role.yaml -- infisicaldynamicsecret_viewer_role.yaml -- infisicalpushsecretsecret_admin_role.yaml -- infisicalpushsecretsecret_editor_role.yaml -- infisicalpushsecretsecret_viewer_role.yaml -- infisicalsecret_admin_role.yaml -- infisicalsecret_editor_role.yaml -- infisicalsecret_viewer_role.yaml - diff --git a/k8-operator/k8-operator/config/rbac/leader_election_role.yaml b/k8-operator/k8-operator/config/rbac/leader_election_role.yaml deleted file mode 100644 index a86e00ed1..000000000 --- a/k8-operator/k8-operator/config/rbac/leader_election_role.yaml +++ /dev/null @@ -1,40 +0,0 @@ -# permissions to do leader election. -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - labels: - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize - name: leader-election-role -rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch diff --git a/k8-operator/k8-operator/config/rbac/leader_election_role_binding.yaml b/k8-operator/k8-operator/config/rbac/leader_election_role_binding.yaml deleted file mode 100644 index d662fc9de..000000000 --- a/k8-operator/k8-operator/config/rbac/leader_election_role_binding.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - labels: - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize - name: leader-election-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: leader-election-role -subjects: -- kind: ServiceAccount - name: controller-manager - namespace: system diff --git a/k8-operator/k8-operator/config/rbac/role.yaml b/k8-operator/k8-operator/config/rbac/role.yaml deleted file mode 100644 index ff39d7b3b..000000000 --- a/k8-operator/k8-operator/config/rbac/role.yaml +++ /dev/null @@ -1,38 +0,0 @@ ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: manager-role -rules: -- apiGroups: - - secrets.infisical.com - resources: - - infisicaldynamicsecrets - - infisicalpushsecretsecrets - - infisicalsecrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - secrets.infisical.com - resources: - - infisicaldynamicsecrets/finalizers - - infisicalpushsecretsecrets/finalizers - - infisicalsecrets/finalizers - verbs: - - update -- apiGroups: - - secrets.infisical.com - resources: - - infisicaldynamicsecrets/status - - infisicalpushsecretsecrets/status - - infisicalsecrets/status - verbs: - - get - - patch - - update diff --git a/k8-operator/k8-operator/config/rbac/role_binding.yaml b/k8-operator/k8-operator/config/rbac/role_binding.yaml deleted file mode 100644 index 5e15ad6f4..000000000 --- a/k8-operator/k8-operator/config/rbac/role_binding.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize - name: manager-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: manager-role -subjects: -- kind: ServiceAccount - name: controller-manager - namespace: system diff --git a/k8-operator/k8-operator/config/rbac/service_account.yaml b/k8-operator/k8-operator/config/rbac/service_account.yaml deleted file mode 100644 index ed238fe99..000000000 --- a/k8-operator/k8-operator/config/rbac/service_account.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize - name: controller-manager - namespace: system diff --git a/k8-operator/k8-operator/config/samples/kustomization.yaml b/k8-operator/k8-operator/config/samples/kustomization.yaml deleted file mode 100644 index d5ed6c144..000000000 --- a/k8-operator/k8-operator/config/samples/kustomization.yaml +++ /dev/null @@ -1,6 +0,0 @@ -## Append samples of your project ## -resources: -- secrets_v1alpha1_infisicalsecret.yaml -- secrets_v1alpha1_infisicalpushsecretsecret.yaml -- secrets_v1alpha1_infisicaldynamicsecret.yaml -# +kubebuilder:scaffold:manifestskustomizesamples diff --git a/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicaldynamicsecret.yaml b/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicaldynamicsecret.yaml deleted file mode 100644 index fd3b990b3..000000000 --- a/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicaldynamicsecret.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: secrets.infisical.com/v1alpha1 -kind: InfisicalDynamicSecret -metadata: - labels: - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize - name: infisicaldynamicsecret-sample -spec: - # TODO(user): Add fields here diff --git a/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalpushsecretsecret.yaml b/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalpushsecretsecret.yaml deleted file mode 100644 index 3370bcc4a..000000000 --- a/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalpushsecretsecret.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: secrets.infisical.com/v1alpha1 -kind: InfisicalPushSecretSecret -metadata: - labels: - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize - name: infisicalpushsecretsecret-sample -spec: - # TODO(user): Add fields here diff --git a/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalsecret.yaml b/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalsecret.yaml deleted file mode 100644 index 029dd4ec0..000000000 --- a/k8-operator/k8-operator/config/samples/secrets_v1alpha1_infisicalsecret.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: secrets.infisical.com/v1alpha1 -kind: InfisicalSecret -metadata: - labels: - app.kubernetes.io/name: k8-operator - app.kubernetes.io/managed-by: kustomize - name: infisicalsecret-sample -spec: - # TODO(user): Add fields here diff --git a/k8-operator/k8-operator/go.mod b/k8-operator/k8-operator/go.mod deleted file mode 100644 index d5c10e6c6..000000000 --- a/k8-operator/k8-operator/go.mod +++ /dev/null @@ -1,97 +0,0 @@ -module github.com/Infisical/infisical/k8-operator - -go 1.24.0 - -require ( - github.com/onsi/ginkgo/v2 v2.22.0 - github.com/onsi/gomega v1.36.1 - k8s.io/apimachinery v0.33.0 - k8s.io/client-go v0.33.0 - sigs.k8s.io/controller-runtime v0.21.0 -) - -require ( - cel.dev/expr v0.19.1 // indirect - github.com/antlr4-go/antlr/v4 v4.13.0 // indirect - github.com/beorn7/perks v1.0.1 // indirect - github.com/blang/semver/v4 v4.0.0 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.11.0 // indirect - github.com/evanphx/json-patch/v5 v5.9.11 // indirect - github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect - github.com/go-logr/logr v1.4.2 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-logr/zapr v1.3.0 // indirect - github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/swag v0.23.0 // indirect - github.com/go-task/slim-sprig/v3 v3.0.0 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/google/btree v1.1.3 // indirect - github.com/google/cel-go v0.23.2 // indirect - github.com/google/gnostic-models v0.6.9 // indirect - github.com/google/go-cmp v0.7.0 // indirect - github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect - github.com/google/uuid v1.6.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 // 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/mailru/easyjson v0.7.7 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.22.0 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.62.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - github.com/spf13/cobra v1.8.1 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/stoewer/go-strcase v1.3.0 // indirect - github.com/x448/float16 v0.8.4 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect - go.opentelemetry.io/otel v1.33.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 // indirect - go.opentelemetry.io/otel/metric v1.33.0 // indirect - go.opentelemetry.io/otel/sdk v1.33.0 // indirect - go.opentelemetry.io/otel/trace v1.33.0 // indirect - go.opentelemetry.io/proto/otlp v1.4.0 // indirect - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect - golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect - golang.org/x/net v0.38.0 // indirect - golang.org/x/oauth2 v0.27.0 // indirect - golang.org/x/sync v0.12.0 // indirect - golang.org/x/sys v0.31.0 // indirect - golang.org/x/term v0.30.0 // indirect - golang.org/x/text v0.23.0 // indirect - golang.org/x/time v0.9.0 // indirect - golang.org/x/tools v0.26.0 // indirect - gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 // indirect - google.golang.org/grpc v1.68.1 // indirect - google.golang.org/protobuf v1.36.5 // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.33.0 // indirect - k8s.io/apiextensions-apiserver v0.33.0 // indirect - k8s.io/apiserver v0.33.0 // indirect - k8s.io/component-base v0.33.0 // indirect - k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect - k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect - sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 // indirect - sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect - sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect - sigs.k8s.io/yaml v1.4.0 // indirect -) diff --git a/k8-operator/k8-operator/go.sum b/k8-operator/k8-operator/go.sum deleted file mode 100644 index 14ef04932..000000000 --- a/k8-operator/k8-operator/go.sum +++ /dev/null @@ -1,254 +0,0 @@ -cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= -cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= -github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= -github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cpuguy83/go-md2man/v2 v2.0.4/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/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= -github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= -github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= -github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= -github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= -github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= -github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= -github.com/go-openapi/jsonreference v0.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/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= -github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= -github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= -github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= -github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= -github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= -github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= -github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= -github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= -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/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/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= -github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= -github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= -github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= -github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -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/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= -github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= -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/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= -github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= -go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= -go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= -go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= -go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= -go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= -go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= -go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= -go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= -go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= -golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= -golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= -golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= -golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= -gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= -google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= -google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= -google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= -gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/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= -k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU= -k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM= -k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs= -k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc= -k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ= -k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= -k8s.io/apiserver v0.33.0 h1:QqcM6c+qEEjkOODHppFXRiw/cE2zP85704YrQ9YaBbc= -k8s.io/apiserver v0.33.0/go.mod h1:EixYOit0YTxt8zrO2kBU7ixAtxFce9gKGq367nFmqI8= -k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98= -k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg= -k8s.io/component-base v0.33.0 h1:Ot4PyJI+0JAD9covDhwLp9UNkUja209OzsJ4FzScBNk= -k8s.io/component-base v0.33.0/go.mod h1:aXYZLbw3kihdkOPMDhWbjGCO6sg+luw554KP51t8qCU= -k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= -k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= -k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= -k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= -k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= -sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= -sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= -sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= -sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= -sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/k8-operator/k8-operator/hack/boilerplate.go.txt b/k8-operator/k8-operator/hack/boilerplate.go.txt deleted file mode 100644 index 221dcbe0b..000000000 --- a/k8-operator/k8-operator/hack/boilerplate.go.txt +++ /dev/null @@ -1,15 +0,0 @@ -/* -Copyright 2025. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ \ No newline at end of file diff --git a/k8-operator/k8-operator/internal/controller/infisicaldynamicsecret_controller.go b/k8-operator/k8-operator/internal/controller/infisicaldynamicsecret_controller.go deleted file mode 100644 index 0930a9064..000000000 --- a/k8-operator/k8-operator/internal/controller/infisicaldynamicsecret_controller.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2025. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package controller - -import ( - "context" - - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - logf "sigs.k8s.io/controller-runtime/pkg/log" - - secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" -) - -// InfisicalDynamicSecretReconciler reconciles a InfisicalDynamicSecret object -type InfisicalDynamicSecretReconciler struct { - client.Client - Scheme *runtime.Scheme -} - -// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/status,verbs=get;update;patch -// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicaldynamicsecrets/finalizers,verbs=update - -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the InfisicalDynamicSecret object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/reconcile -func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - _ = logf.FromContext(ctx) - - // TODO(user): your logic here - - return ctrl.Result{}, nil -} - -// SetupWithManager sets up the controller with the Manager. -func (r *InfisicalDynamicSecretReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&secretsv1alpha1.InfisicalDynamicSecret{}). - Named("infisicaldynamicsecret"). - Complete(r) -} diff --git a/k8-operator/k8-operator/internal/controller/infisicalpushsecretsecret_controller.go b/k8-operator/k8-operator/internal/controller/infisicalpushsecretsecret_controller.go deleted file mode 100644 index 467865e79..000000000 --- a/k8-operator/k8-operator/internal/controller/infisicalpushsecretsecret_controller.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2025. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package controller - -import ( - "context" - - "k8s.io/apimachinery/pkg/runtime" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - logf "sigs.k8s.io/controller-runtime/pkg/log" - - secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" -) - -// InfisicalPushSecretSecretReconciler reconciles a InfisicalPushSecretSecret object -type InfisicalPushSecretSecretReconciler struct { - client.Client - Scheme *runtime.Scheme -} - -// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalpushsecretsecrets,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalpushsecretsecrets/status,verbs=get;update;patch -// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalpushsecretsecrets/finalizers,verbs=update - -// Reconcile is part of the main kubernetes reconciliation loop which aims to -// move the current state of the cluster closer to the desired state. -// TODO(user): Modify the Reconcile function to compare the state specified by -// the InfisicalPushSecretSecret object against the actual cluster state, and then -// perform operations to make the cluster state reflect the state specified by -// the user. -// -// For more details, check Reconcile and its Result here: -// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/reconcile -func (r *InfisicalPushSecretSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - _ = logf.FromContext(ctx) - - // TODO(user): your logic here - - return ctrl.Result{}, nil -} - -// SetupWithManager sets up the controller with the Manager. -func (r *InfisicalPushSecretSecretReconciler) SetupWithManager(mgr ctrl.Manager) error { - return ctrl.NewControllerManagedBy(mgr). - For(&secretsv1alpha1.InfisicalPushSecretSecret{}). - Named("infisicalpushsecretsecret"). - Complete(r) -} diff --git a/k8-operator/k8-operator/internal/model/model.go b/k8-operator/k8-operator/internal/model/model.go deleted file mode 100644 index 3b3bc3216..000000000 --- a/k8-operator/k8-operator/internal/model/model.go +++ /dev/null @@ -1,37 +0,0 @@ -package model - -type ServiceAccountDetails struct { - AccessKey string - PublicKey string - PrivateKey string -} - -type MachineIdentityDetails struct { - ClientId string - ClientSecret string -} - -type SingleEnvironmentVariable struct { - Key string `json:"key"` - Value string `json:"value"` - SecretPath string `json:"secretPath"` - Type string `json:"type"` - ID string `json:"id"` -} - -type SecretTemplateOptions struct { - Value string `json:"value"` - SecretPath string `json:"secretPath"` -} - -type Project struct { - ID string `json:"id"` - Name string `json:"name"` - Slug string `json:"slug"` - OrgID string `json:"orgId"` - Environments []struct { - Name string `json:"name"` - Slug string `json:"slug"` - ID string `json:"id"` - } -} diff --git a/k8-operator/k8-operator/internal/util/auth.go b/k8-operator/k8-operator/internal/util/auth.go deleted file mode 100644 index 7305ca45e..000000000 --- a/k8-operator/k8-operator/internal/util/auth.go +++ /dev/null @@ -1,490 +0,0 @@ -package util - -import ( - "context" - "fmt" - - "errors" - - corev1 "k8s.io/api/core/v1" - - authenticationv1 "k8s.io/api/authentication/v1" - - "github.com/Infisical/infisical/k8-operator/api/v1alpha1" - "github.com/aws/smithy-go/ptr" - infisicalSdk "github.com/infisical/go-sdk" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func GetServiceAccountToken(k8sClient client.Client, namespace string, serviceAccountName string, autoCreateServiceAccountToken bool, serviceAccountTokenAudiences []string) (string, error) { - - if autoCreateServiceAccountToken { - restClient, err := GetRestClientFromClient() - if err != nil { - return "", fmt.Errorf("failed to get REST client: %w", err) - } - - tokenRequest := &authenticationv1.TokenRequest{ - Spec: authenticationv1.TokenRequestSpec{ - ExpirationSeconds: ptr.Int64(600), // 10 minutes. the token only needs to be valid for when we do the initial k8s login. - }, - } - - if len(serviceAccountTokenAudiences) > 0 { - // Conditionally add the audiences if they are specified. - // Failing to do this causes a default audience to be used, which is not what we want if the user doesn't specify any. - tokenRequest.Spec.Audiences = serviceAccountTokenAudiences - } - - result := &authenticationv1.TokenRequest{} - err = restClient. - Post(). - Namespace(namespace). - Resource("serviceaccounts"). - Name(serviceAccountName). - SubResource("token"). - Body(tokenRequest). - Do(context.Background()). - Into(result) - - if err != nil { - return "", fmt.Errorf("failed to create token: %w", err) - } - - return result.Status.Token, nil - } - - serviceAccount := &corev1.ServiceAccount{} - err := k8sClient.Get(context.TODO(), client.ObjectKey{Name: serviceAccountName, Namespace: namespace}, serviceAccount) - if err != nil { - return "", err - } - - if len(serviceAccount.Secrets) == 0 { - return "", fmt.Errorf("no secrets found for service account %s", serviceAccountName) - } - - secretName := serviceAccount.Secrets[0].Name - - secret := &corev1.Secret{} - err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: secretName, Namespace: namespace}, secret) - if err != nil { - return "", err - } - - token := secret.Data["token"] - - return string(token), nil -} - -type AuthStrategyType string - -var AuthStrategy = struct { - SERVICE_TOKEN AuthStrategyType - SERVICE_ACCOUNT AuthStrategyType - UNIVERSAL_MACHINE_IDENTITY AuthStrategyType - KUBERNETES_MACHINE_IDENTITY AuthStrategyType - AWS_IAM_MACHINE_IDENTITY AuthStrategyType - AZURE_MACHINE_IDENTITY AuthStrategyType - GCP_ID_TOKEN_MACHINE_IDENTITY AuthStrategyType - GCP_IAM_MACHINE_IDENTITY AuthStrategyType -}{ - SERVICE_TOKEN: "SERVICE_TOKEN", - SERVICE_ACCOUNT: "SERVICE_ACCOUNT", - UNIVERSAL_MACHINE_IDENTITY: "UNIVERSAL_MACHINE_IDENTITY", - KUBERNETES_MACHINE_IDENTITY: "KUBERNETES_AUTH_MACHINE_IDENTITY", - AWS_IAM_MACHINE_IDENTITY: "AWS_IAM_MACHINE_IDENTITY", - AZURE_MACHINE_IDENTITY: "AZURE_MACHINE_IDENTITY", - GCP_ID_TOKEN_MACHINE_IDENTITY: "GCP_ID_TOKEN_MACHINE_IDENTITY", - GCP_IAM_MACHINE_IDENTITY: "GCP_IAM_MACHINE_IDENTITY", -} - -type SecretCrdType string - -var SecretCrd = struct { - INFISICAL_SECRET SecretCrdType - INFISICAL_PUSH_SECRET SecretCrdType - INFISICAL_DYNAMIC_SECRET SecretCrdType -}{ - INFISICAL_SECRET: "INFISICAL_SECRET", - INFISICAL_PUSH_SECRET: "INFISICAL_PUSH_SECRET", - INFISICAL_DYNAMIC_SECRET: "INFISICAL_DYNAMIC_SECRET", -} - -type SecretAuthInput struct { - Secret interface{} - Type SecretCrdType -} - -type AuthenticationDetails struct { - AuthStrategy AuthStrategyType - MachineIdentityScope v1alpha1.MachineIdentityScopeInWorkspace // This will only be set if a machine identity auth method is used (e.g. UniversalAuth or KubernetesAuth, etc.) - IsMachineIdentityAuth bool - SecretType SecretCrdType -} - -var ErrAuthNotApplicable = errors.New("authentication not applicable") - -func HandleUniversalAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { - - var universalAuthSpec v1alpha1.UniversalAuthDetails - - switch secretCrd.Type { - case SecretCrd.INFISICAL_SECRET: - infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret") - } - universalAuthSpec = infisicalSecret.Spec.Authentication.UniversalAuth - case SecretCrd.INFISICAL_PUSH_SECRET: - infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret") - } - - universalAuthSpec = v1alpha1.UniversalAuthDetails{ - CredentialsRef: infisicalPushSecret.Spec.Authentication.UniversalAuth.CredentialsRef, - SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, - } - - case SecretCrd.INFISICAL_DYNAMIC_SECRET: - infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret") - } - - universalAuthSpec = v1alpha1.UniversalAuthDetails{ - CredentialsRef: infisicalDynamicSecret.Spec.Authentication.UniversalAuth.CredentialsRef, - SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, - } - } - - universalAuthKubeSecret, err := GetInfisicalUniversalAuthFromKubeSecret(ctx, reconcilerClient, v1alpha1.KubeSecretReference{ - SecretNamespace: universalAuthSpec.CredentialsRef.SecretNamespace, - SecretName: universalAuthSpec.CredentialsRef.SecretName, - }) - - if err != nil { - return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get machine identity creds from kube secret [err=%s]", err) - } - - if universalAuthKubeSecret.ClientId == "" && universalAuthKubeSecret.ClientSecret == "" { - return AuthenticationDetails{}, ErrAuthNotApplicable - } - - _, err = infisicalClient.Auth().UniversalAuthLogin(universalAuthKubeSecret.ClientId, universalAuthKubeSecret.ClientSecret) - if err != nil { - return AuthenticationDetails{}, fmt.Errorf("unable to login with machine identity credentials [err=%s]", err) - } - - return AuthenticationDetails{ - AuthStrategy: AuthStrategy.UNIVERSAL_MACHINE_IDENTITY, - MachineIdentityScope: universalAuthSpec.SecretsScope, - IsMachineIdentityAuth: true, - SecretType: secretCrd.Type, - }, nil -} - -func HandleKubernetesAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { - var kubernetesAuthSpec v1alpha1.KubernetesAuthDetails - - switch secretCrd.Type { - case SecretCrd.INFISICAL_SECRET: - infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret") - } - kubernetesAuthSpec = infisicalSecret.Spec.Authentication.KubernetesAuth - case SecretCrd.INFISICAL_PUSH_SECRET: - infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret") - } - kubernetesAuthSpec = v1alpha1.KubernetesAuthDetails{ - IdentityID: infisicalPushSecret.Spec.Authentication.KubernetesAuth.IdentityID, - ServiceAccountRef: v1alpha1.KubernetesServiceAccountRef{ - Namespace: infisicalPushSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Namespace, - Name: infisicalPushSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Name, - }, - SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, - AutoCreateServiceAccountToken: infisicalPushSecret.Spec.Authentication.KubernetesAuth.AutoCreateServiceAccountToken, - ServiceAccountTokenAudiences: infisicalPushSecret.Spec.Authentication.KubernetesAuth.ServiceAccountTokenAudiences, - } - - case SecretCrd.INFISICAL_DYNAMIC_SECRET: - infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret") - } - - kubernetesAuthSpec = v1alpha1.KubernetesAuthDetails{ - IdentityID: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.IdentityID, - ServiceAccountRef: v1alpha1.KubernetesServiceAccountRef{ - Namespace: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Namespace, - Name: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.ServiceAccountRef.Name, - }, - SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, - AutoCreateServiceAccountToken: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.AutoCreateServiceAccountToken, - ServiceAccountTokenAudiences: infisicalDynamicSecret.Spec.Authentication.KubernetesAuth.ServiceAccountTokenAudiences, - } - } - - if kubernetesAuthSpec.IdentityID == "" { - return AuthenticationDetails{}, ErrAuthNotApplicable - } - - serviceAccountToken, err := GetServiceAccountToken( - reconcilerClient, - kubernetesAuthSpec.ServiceAccountRef.Namespace, - kubernetesAuthSpec.ServiceAccountRef.Name, - kubernetesAuthSpec.AutoCreateServiceAccountToken, - kubernetesAuthSpec.ServiceAccountTokenAudiences, - ) - - if err != nil { - return AuthenticationDetails{}, fmt.Errorf("unable to get service account token [err=%s]", err) - } - - _, err = infisicalClient.Auth().KubernetesRawServiceAccountTokenLogin(kubernetesAuthSpec.IdentityID, serviceAccountToken) - if err != nil { - return AuthenticationDetails{}, fmt.Errorf("unable to login with Kubernetes native auth [err=%s]", err) - } - - return AuthenticationDetails{ - AuthStrategy: AuthStrategy.KUBERNETES_MACHINE_IDENTITY, - MachineIdentityScope: kubernetesAuthSpec.SecretsScope, - IsMachineIdentityAuth: true, - SecretType: secretCrd.Type, - }, nil - -} - -func HandleAwsIamAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { - awsIamAuthSpec := v1alpha1.AWSIamAuthDetails{} - - switch secretCrd.Type { - case SecretCrd.INFISICAL_SECRET: - infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret") - } - - awsIamAuthSpec = infisicalSecret.Spec.Authentication.AwsIamAuth - case SecretCrd.INFISICAL_PUSH_SECRET: - infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret") - } - - awsIamAuthSpec = v1alpha1.AWSIamAuthDetails{ - IdentityID: infisicalPushSecret.Spec.Authentication.AwsIamAuth.IdentityID, - SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, - } - - case SecretCrd.INFISICAL_DYNAMIC_SECRET: - infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret") - } - - awsIamAuthSpec = v1alpha1.AWSIamAuthDetails{ - IdentityID: infisicalDynamicSecret.Spec.Authentication.AwsIamAuth.IdentityID, - SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, - } - } - - if awsIamAuthSpec.IdentityID == "" { - return AuthenticationDetails{}, ErrAuthNotApplicable - } - - _, err := infisicalClient.Auth().AwsIamAuthLogin(awsIamAuthSpec.IdentityID) - if err != nil { - return AuthenticationDetails{}, fmt.Errorf("unable to login with AWS IAM auth [err=%s]", err) - } - - return AuthenticationDetails{ - AuthStrategy: AuthStrategy.AWS_IAM_MACHINE_IDENTITY, - MachineIdentityScope: awsIamAuthSpec.SecretsScope, - IsMachineIdentityAuth: true, - SecretType: secretCrd.Type, - }, nil - -} - -func HandleAzureAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { - azureAuthSpec := v1alpha1.AzureAuthDetails{} - - switch secretCrd.Type { - case SecretCrd.INFISICAL_SECRET: - infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret") - } - - azureAuthSpec = infisicalSecret.Spec.Authentication.AzureAuth - - case SecretCrd.INFISICAL_PUSH_SECRET: - infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret") - } - - azureAuthSpec = v1alpha1.AzureAuthDetails{ - IdentityID: infisicalPushSecret.Spec.Authentication.AzureAuth.IdentityID, - Resource: infisicalPushSecret.Spec.Authentication.AzureAuth.Resource, - SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, - } - - case SecretCrd.INFISICAL_DYNAMIC_SECRET: - infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret") - } - - azureAuthSpec = v1alpha1.AzureAuthDetails{ - IdentityID: infisicalDynamicSecret.Spec.Authentication.AzureAuth.IdentityID, - Resource: infisicalDynamicSecret.Spec.Authentication.AzureAuth.Resource, - SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, - } - } - - if azureAuthSpec.IdentityID == "" { - return AuthenticationDetails{}, ErrAuthNotApplicable - } - - _, err := infisicalClient.Auth().AzureAuthLogin(azureAuthSpec.IdentityID, azureAuthSpec.Resource) // If resource is empty(""), it will default to "https://management.azure.com/" in the SDK. - if err != nil { - return AuthenticationDetails{}, fmt.Errorf("unable to login with Azure auth [err=%s]", err) - } - - return AuthenticationDetails{ - AuthStrategy: AuthStrategy.AZURE_MACHINE_IDENTITY, - MachineIdentityScope: azureAuthSpec.SecretsScope, - IsMachineIdentityAuth: true, - SecretType: secretCrd.Type, - }, nil - -} - -func HandleGcpIdTokenAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { - gcpIdTokenSpec := v1alpha1.GCPIdTokenAuthDetails{} - - switch secretCrd.Type { - case SecretCrd.INFISICAL_SECRET: - infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret") - } - - gcpIdTokenSpec = infisicalSecret.Spec.Authentication.GcpIdTokenAuth - case SecretCrd.INFISICAL_PUSH_SECRET: - infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret") - } - - gcpIdTokenSpec = v1alpha1.GCPIdTokenAuthDetails{ - IdentityID: infisicalPushSecret.Spec.Authentication.GcpIdTokenAuth.IdentityID, - SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, - } - - case SecretCrd.INFISICAL_DYNAMIC_SECRET: - infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret") - } - - gcpIdTokenSpec = v1alpha1.GCPIdTokenAuthDetails{ - IdentityID: infisicalDynamicSecret.Spec.Authentication.GcpIdTokenAuth.IdentityID, - SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, - } - } - - if gcpIdTokenSpec.IdentityID == "" { - return AuthenticationDetails{}, ErrAuthNotApplicable - } - - _, err := infisicalClient.Auth().GcpIdTokenAuthLogin(gcpIdTokenSpec.IdentityID) - if err != nil { - return AuthenticationDetails{}, fmt.Errorf("unable to login with GCP Id Token auth [err=%s]", err) - } - - return AuthenticationDetails{ - AuthStrategy: AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY, - MachineIdentityScope: gcpIdTokenSpec.SecretsScope, - IsMachineIdentityAuth: true, - SecretType: secretCrd.Type, - }, nil - -} - -func HandleGcpIamAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { - gcpIamSpec := v1alpha1.GcpIamAuthDetails{} - - switch secretCrd.Type { - case SecretCrd.INFISICAL_SECRET: - infisicalSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret") - } - - gcpIamSpec = infisicalSecret.Spec.Authentication.GcpIamAuth - case SecretCrd.INFISICAL_PUSH_SECRET: - infisicalPushSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalPushSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalPushSecret") - } - - gcpIamSpec = v1alpha1.GcpIamAuthDetails{ - IdentityID: infisicalPushSecret.Spec.Authentication.GcpIamAuth.IdentityID, - ServiceAccountKeyFilePath: infisicalPushSecret.Spec.Authentication.GcpIamAuth.ServiceAccountKeyFilePath, - SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, - } - - case SecretCrd.INFISICAL_DYNAMIC_SECRET: - infisicalDynamicSecret, ok := secretCrd.Secret.(v1alpha1.InfisicalDynamicSecret) - - if !ok { - return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalDynamicSecret") - } - - gcpIamSpec = v1alpha1.GcpIamAuthDetails{ - IdentityID: infisicalDynamicSecret.Spec.Authentication.GcpIamAuth.IdentityID, - ServiceAccountKeyFilePath: infisicalDynamicSecret.Spec.Authentication.GcpIamAuth.ServiceAccountKeyFilePath, - SecretsScope: v1alpha1.MachineIdentityScopeInWorkspace{}, - } - } - - if gcpIamSpec.IdentityID == "" && gcpIamSpec.ServiceAccountKeyFilePath == "" { - return AuthenticationDetails{}, ErrAuthNotApplicable - } - - _, err := infisicalClient.Auth().GcpIamAuthLogin(gcpIamSpec.IdentityID, gcpIamSpec.ServiceAccountKeyFilePath) - if err != nil { - return AuthenticationDetails{}, fmt.Errorf("unable to login with GCP IAM auth [err=%s]", err) - } - - return AuthenticationDetails{ - AuthStrategy: AuthStrategy.GCP_IAM_MACHINE_IDENTITY, - MachineIdentityScope: gcpIamSpec.SecretsScope, - IsMachineIdentityAuth: true, - SecretType: secretCrd.Type, - }, nil -} diff --git a/k8-operator/kubectl-install/install-secrets-operator.yaml b/k8-operator/kubectl-install/install-secrets-operator.yaml index 49ac1466e..2956fe570 100644 --- a/k8-operator/kubectl-install/install-secrets-operator.yaml +++ b/k8-operator/kubectl-install/install-secrets-operator.yaml @@ -27,252 +27,252 @@ spec: singular: infisicaldynamicsecret scope: Namespaced versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: InfisicalDynamicSecret is the Schema for the infisicaldynamicsecrets 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: InfisicalDynamicSecretSpec defines the desired state of InfisicalDynamicSecret. - properties: - authentication: - properties: - awsIamAuth: - properties: - identityId: - type: string - required: - - identityId - type: object - azureAuth: - properties: - identityId: - type: string - resource: - type: string - required: - - identityId - type: object - gcpIamAuth: - properties: - identityId: - type: string - serviceAccountKeyFilePath: - type: string - required: - - identityId - - serviceAccountKeyFilePath - type: object - gcpIdTokenAuth: - properties: - identityId: - type: string - required: - - identityId - type: object - kubernetesAuth: - properties: - identityId: - type: string - serviceAccountRef: - properties: - name: - type: string - namespace: - type: string - required: - - name - - namespace - type: object - required: - - identityId - - serviceAccountRef - type: object - universalAuth: - properties: - credentialsRef: - properties: - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret is located - type: string - required: - - secretName - - secretNamespace - type: object - required: - - credentialsRef - type: object - type: object - dynamicSecret: - properties: - environmentSlug: - type: string - projectId: - type: string - secretName: - type: string - secretsPath: - type: string - required: - - environmentSlug - - projectId - - secretName - - secretsPath - type: object - hostAPI: - type: string - leaseRevocationPolicy: - type: string - leaseTTL: - type: string - managedSecretReference: - properties: - creationPolicy: - default: Orphan - description: 'The Kubernetes Secret creation policy. Enum with values: ''Owner'', ''Orphan''. Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted.' - type: string - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret is located - type: string - secretType: - default: Opaque - description: 'The Kubernetes Secret type (experimental feature). More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types' - type: string - template: - description: The template to transform the secret data - properties: - data: - additionalProperties: - type: string - description: The template key values - type: object - includeAllSecrets: - description: This injects all retrieved secrets into the top level of your template. Secrets defined in the template will take precedence over the injected ones. - type: boolean - type: object - required: - - secretName - - secretNamespace - type: object - tls: - properties: - caRef: - description: Reference to secret containing CA cert - properties: - key: - description: The name of the secret property with the CA certificate value - type: string - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The namespace where the Kubernetes Secret is located - type: string - required: - - key - - secretName - - secretNamespace - type: object - type: object - required: - - authentication - - dynamicSecret - - leaseRevocationPolicy - - leaseTTL - - managedSecretReference - type: object - status: - description: InfisicalDynamicSecretStatus defines the observed state of InfisicalDynamicSecret. - properties: - conditions: - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + - name: v1alpha1 + schema: + openAPIV3Schema: + description: InfisicalDynamicSecret is the Schema for the infisicaldynamicsecrets 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: InfisicalDynamicSecretSpec defines the desired state of InfisicalDynamicSecret. + properties: + authentication: properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time + awsIamAuth: + properties: + identityId: + type: string + required: + - identityId + type: object + azureAuth: + properties: + identityId: + type: string + resource: + type: string + required: + - identityId + type: object + gcpIamAuth: + properties: + identityId: + type: string + serviceAccountKeyFilePath: + type: string + required: + - identityId + - serviceAccountKeyFilePath + type: object + gcpIdTokenAuth: + properties: + identityId: + type: string + required: + - identityId + type: object + kubernetesAuth: + properties: + identityId: + type: string + serviceAccountRef: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + required: + - identityId + - serviceAccountRef + type: object + universalAuth: + properties: + credentialsRef: + properties: + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret is located + type: string + required: + - secretName + - secretNamespace + type: object + required: + - credentialsRef + type: object + type: object + dynamicSecret: + properties: + environmentSlug: type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 + projectId: type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + secretName: type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + secretsPath: type: string required: - - lastTransitionTime - - message - - reason - - status - - type + - environmentSlug + - projectId + - secretName + - secretsPath type: object - type: array - dynamicSecretId: - type: string - lease: - properties: - creationTimestamp: - format: date-time - type: string - expiresAt: - format: date-time - type: string - id: - type: string - version: - format: int64 - type: integer - required: - - creationTimestamp - - expiresAt - - id - - version - type: object - maxTTL: - description: The MaxTTL can be null, if it's null, there's no max TTL and we should never have to renew. - type: string - required: - - conditions - type: object - type: object - served: true - storage: true - subresources: - status: {} + hostAPI: + type: string + leaseRevocationPolicy: + type: string + leaseTTL: + type: string + managedSecretReference: + properties: + creationPolicy: + default: Orphan + description: "The Kubernetes Secret creation policy. Enum with values: 'Owner', 'Orphan'. Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted." + type: string + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret is located + type: string + secretType: + default: Opaque + description: "The Kubernetes Secret type (experimental feature). More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types" + type: string + template: + description: The template to transform the secret data + properties: + data: + additionalProperties: + type: string + description: The template key values + type: object + includeAllSecrets: + description: This injects all retrieved secrets into the top level of your template. Secrets defined in the template will take precedence over the injected ones. + type: boolean + type: object + required: + - secretName + - secretNamespace + type: object + tls: + properties: + caRef: + description: Reference to secret containing CA cert + properties: + key: + description: The name of the secret property with the CA certificate value + type: string + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The namespace where the Kubernetes Secret is located + type: string + required: + - key + - secretName + - secretNamespace + type: object + type: object + required: + - authentication + - dynamicSecret + - leaseRevocationPolicy + - leaseTTL + - managedSecretReference + type: object + status: + description: InfisicalDynamicSecretStatus defines the observed state of InfisicalDynamicSecret. + properties: + conditions: + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + dynamicSecretId: + type: string + lease: + properties: + creationTimestamp: + format: date-time + type: string + expiresAt: + format: date-time + type: string + id: + type: string + version: + format: int64 + type: integer + required: + - creationTimestamp + - expiresAt + - id + - version + type: object + maxTTL: + description: The MaxTTL can be null, if it's null, there's no max TTL and we should never have to renew. + type: string + required: + - conditions + type: object + type: object + served: true + storage: true + subresources: + status: {} --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -290,217 +290,217 @@ spec: singular: infisicalpushsecret scope: Namespaced versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: InfisicalPushSecret is the Schema for the infisicalpushsecrets 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: InfisicalPushSecretSpec defines the desired state of InfisicalPushSecret - properties: - authentication: - properties: - awsIamAuth: - properties: - identityId: - type: string - required: - - identityId - type: object - azureAuth: - properties: - identityId: - type: string - resource: - type: string - required: - - identityId - type: object - gcpIamAuth: - properties: - identityId: - type: string - serviceAccountKeyFilePath: - type: string - required: - - identityId - - serviceAccountKeyFilePath - type: object - gcpIdTokenAuth: - properties: - identityId: - type: string - required: - - identityId - type: object - kubernetesAuth: - properties: - identityId: - type: string - serviceAccountRef: - properties: - name: - type: string - namespace: - type: string - required: - - name - - namespace - type: object - required: - - identityId - - serviceAccountRef - type: object - universalAuth: - properties: - credentialsRef: - properties: - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret is located - type: string - required: - - secretName - - secretNamespace - type: object - required: - - credentialsRef - type: object - type: object - deletionPolicy: - type: string - destination: - properties: - environmentSlug: - type: string - projectId: - type: string - secretsPath: - type: string - required: - - environmentSlug - - projectId - - secretsPath - type: object - hostAPI: - description: Infisical host to pull secrets from - type: string - push: - properties: - secret: - properties: - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret is located - type: string - required: - - secretName - - secretNamespace - type: object - required: - - secret - type: object - resyncInterval: - type: string - tls: - properties: - caRef: - description: Reference to secret containing CA cert - properties: - key: - description: The name of the secret property with the CA certificate value - type: string - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The namespace where the Kubernetes Secret is located - type: string - required: - - key - - secretName - - secretNamespace - type: object - type: object - updatePolicy: - type: string - required: - - destination - - push - - resyncInterval - type: object - status: - description: InfisicalPushSecretStatus defines the observed state of InfisicalPushSecret - properties: - conditions: - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + - name: v1alpha1 + schema: + openAPIV3Schema: + description: InfisicalPushSecret is the Schema for the infisicalpushsecrets 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: InfisicalPushSecretSpec defines the desired state of InfisicalPushSecret + properties: + authentication: properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time + awsIamAuth: + properties: + identityId: + type: string + required: + - identityId + type: object + azureAuth: + properties: + identityId: + type: string + resource: + type: string + required: + - identityId + type: object + gcpIamAuth: + properties: + identityId: + type: string + serviceAccountKeyFilePath: + type: string + required: + - identityId + - serviceAccountKeyFilePath + type: object + gcpIdTokenAuth: + properties: + identityId: + type: string + required: + - identityId + type: object + kubernetesAuth: + properties: + identityId: + type: string + serviceAccountRef: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + required: + - identityId + - serviceAccountRef + type: object + universalAuth: + properties: + credentialsRef: + properties: + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret is located + type: string + required: + - secretName + - secretNamespace + type: object + required: + - credentialsRef + type: object + type: object + deletionPolicy: + type: string + destination: + properties: + environmentSlug: type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 + projectId: type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + secretsPath: type: string required: - - lastTransitionTime - - message - - reason - - status - - type + - environmentSlug + - projectId + - secretsPath type: object - type: array - managedSecrets: - additionalProperties: + hostAPI: + description: Infisical host to pull secrets from type: string - description: managed secrets is a map where the key is the ID, and the value is the secret key (string[id], string[key] ) - type: object - required: - - conditions - - managedSecrets - type: object - type: object - served: true - storage: true - subresources: - status: {} + push: + properties: + secret: + properties: + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret is located + type: string + required: + - secretName + - secretNamespace + type: object + required: + - secret + type: object + resyncInterval: + type: string + tls: + properties: + caRef: + description: Reference to secret containing CA cert + properties: + key: + description: The name of the secret property with the CA certificate value + type: string + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The namespace where the Kubernetes Secret is located + type: string + required: + - key + - secretName + - secretNamespace + type: object + type: object + updatePolicy: + type: string + required: + - destination + - push + - resyncInterval + type: object + status: + description: InfisicalPushSecretStatus defines the observed state of InfisicalPushSecret + properties: + conditions: + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + managedSecrets: + additionalProperties: + type: string + description: managed secrets is a map where the key is the ID, and the value is the secret key (string[id], string[key] ) + type: object + required: + - conditions + - managedSecrets + type: object + type: object + served: true + storage: true + subresources: + status: {} --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -518,251 +518,284 @@ spec: singular: infisicalsecret scope: Namespaced versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: InfisicalSecret is the Schema for the infisicalsecrets 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: InfisicalSecretSpec defines the desired state of InfisicalSecret - properties: - authentication: - properties: - awsIamAuth: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: InfisicalSecret is the Schema for the infisicalsecrets 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: InfisicalSecretSpec defines the desired state of InfisicalSecret + properties: + authentication: + properties: + awsIamAuth: + properties: + identityId: + type: string + secretsScope: + properties: + envSlug: + type: string + projectSlug: + type: string + recursive: + type: boolean + secretsPath: + type: string + required: + - envSlug + - projectSlug + - secretsPath + type: object + required: + - identityId + - secretsScope + type: object + azureAuth: + properties: + identityId: + type: string + resource: + type: string + secretsScope: + properties: + envSlug: + type: string + projectSlug: + type: string + recursive: + type: boolean + secretsPath: + type: string + required: + - envSlug + - projectSlug + - secretsPath + type: object + required: + - identityId + - secretsScope + type: object + gcpIamAuth: + properties: + identityId: + type: string + secretsScope: + properties: + envSlug: + type: string + projectSlug: + type: string + recursive: + type: boolean + secretsPath: + type: string + required: + - envSlug + - projectSlug + - secretsPath + type: object + serviceAccountKeyFilePath: + type: string + required: + - identityId + - secretsScope + - serviceAccountKeyFilePath + type: object + gcpIdTokenAuth: + properties: + identityId: + type: string + secretsScope: + properties: + envSlug: + type: string + projectSlug: + type: string + recursive: + type: boolean + secretsPath: + type: string + required: + - envSlug + - projectSlug + - secretsPath + type: object + required: + - identityId + - secretsScope + type: object + kubernetesAuth: + properties: + identityId: + type: string + secretsScope: + properties: + envSlug: + type: string + projectSlug: + type: string + recursive: + type: boolean + secretsPath: + type: string + required: + - envSlug + - projectSlug + - secretsPath + type: object + serviceAccountRef: + properties: + name: + type: string + namespace: + type: string + required: + - name + - namespace + type: object + required: + - identityId + - secretsScope + - serviceAccountRef + type: object + serviceAccount: + properties: + environmentName: + type: string + projectId: + type: string + serviceAccountSecretReference: + properties: + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret is located + type: string + required: + - secretName + - secretNamespace + type: object + required: + - environmentName + - projectId + - serviceAccountSecretReference + type: object + serviceToken: + properties: + secretsScope: + properties: + envSlug: + type: string + recursive: + type: boolean + secretsPath: + type: string + required: + - envSlug + - secretsPath + type: object + serviceTokenSecretReference: + properties: + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret is located + type: string + required: + - secretName + - secretNamespace + type: object + required: + - secretsScope + - serviceTokenSecretReference + type: object + universalAuth: + properties: + credentialsRef: + properties: + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret is located + type: string + required: + - secretName + - secretNamespace + type: object + secretsScope: + properties: + envSlug: + type: string + projectSlug: + type: string + recursive: + type: boolean + secretsPath: + type: string + required: + - envSlug + - projectSlug + - secretsPath + type: object + required: + - credentialsRef + - secretsScope + type: object + type: object + hostAPI: + description: Infisical host to pull secrets from + type: string + managedKubeSecretReferences: + items: properties: - identityId: + creationPolicy: + default: Orphan + description: "The Kubernetes Secret creation policy. Enum with values: 'Owner', 'Orphan'. Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted." type: string - secretsScope: + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The name space where the Kubernetes Secret is located + type: string + secretType: + default: Opaque + description: "The Kubernetes Secret type (experimental feature). More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types" + type: string + template: + description: The template to transform the secret data properties: - envSlug: - type: string - projectSlug: - type: string - recursive: + data: + additionalProperties: + type: string + description: The template key values + type: object + includeAllSecrets: + description: This injects all retrieved secrets into the top level of your template. Secrets defined in the template will take precedence over the injected ones. type: boolean - secretsPath: - type: string - required: - - envSlug - - projectSlug - - secretsPath type: object required: - - identityId - - secretsScope + - secretName + - secretNamespace type: object - azureAuth: - properties: - identityId: - type: string - resource: - type: string - secretsScope: - properties: - envSlug: - type: string - projectSlug: - type: string - recursive: - type: boolean - secretsPath: - type: string - required: - - envSlug - - projectSlug - - secretsPath - type: object - required: - - identityId - - secretsScope - type: object - gcpIamAuth: - properties: - identityId: - type: string - secretsScope: - properties: - envSlug: - type: string - projectSlug: - type: string - recursive: - type: boolean - secretsPath: - type: string - required: - - envSlug - - projectSlug - - secretsPath - type: object - serviceAccountKeyFilePath: - type: string - required: - - identityId - - secretsScope - - serviceAccountKeyFilePath - type: object - gcpIdTokenAuth: - properties: - identityId: - type: string - secretsScope: - properties: - envSlug: - type: string - projectSlug: - type: string - recursive: - type: boolean - secretsPath: - type: string - required: - - envSlug - - projectSlug - - secretsPath - type: object - required: - - identityId - - secretsScope - type: object - kubernetesAuth: - properties: - identityId: - type: string - secretsScope: - properties: - envSlug: - type: string - projectSlug: - type: string - recursive: - type: boolean - secretsPath: - type: string - required: - - envSlug - - projectSlug - - secretsPath - type: object - serviceAccountRef: - properties: - name: - type: string - namespace: - type: string - required: - - name - - namespace - type: object - required: - - identityId - - secretsScope - - serviceAccountRef - type: object - serviceAccount: - properties: - environmentName: - type: string - projectId: - type: string - serviceAccountSecretReference: - properties: - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret is located - type: string - required: - - secretName - - secretNamespace - type: object - required: - - environmentName - - projectId - - serviceAccountSecretReference - type: object - serviceToken: - properties: - secretsScope: - properties: - envSlug: - type: string - recursive: - type: boolean - secretsPath: - type: string - required: - - envSlug - - secretsPath - type: object - serviceTokenSecretReference: - properties: - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret is located - type: string - required: - - secretName - - secretNamespace - type: object - required: - - secretsScope - - serviceTokenSecretReference - type: object - universalAuth: - properties: - credentialsRef: - properties: - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret is located - type: string - required: - - secretName - - secretNamespace - type: object - secretsScope: - properties: - envSlug: - type: string - projectSlug: - type: string - recursive: - type: boolean - secretsPath: - type: string - required: - - envSlug - - projectSlug - - secretsPath - type: object - required: - - credentialsRef - - secretsScope - type: object - type: object - hostAPI: - description: Infisical host to pull secrets from - type: string - managedKubeSecretReferences: - items: + type: array + managedSecretReference: properties: creationPolicy: default: Orphan - description: 'The Kubernetes Secret creation policy. Enum with values: ''Owner'', ''Orphan''. Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted.' + description: "The Kubernetes Secret creation policy. Enum with values: 'Owner', 'Orphan'. Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted." type: string secretName: description: The name of the Kubernetes Secret @@ -772,7 +805,7 @@ spec: type: string secretType: default: Opaque - description: 'The Kubernetes Secret type (experimental feature). More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types' + description: "The Kubernetes Secret type (experimental feature). More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types" type: string template: description: The template to transform the secret data @@ -787,134 +820,101 @@ spec: type: boolean type: object required: - - secretName - - secretNamespace - type: object - type: array - managedSecretReference: - properties: - creationPolicy: - default: Orphan - description: 'The Kubernetes Secret creation policy. Enum with values: ''Owner'', ''Orphan''. Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted.' - type: string - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret is located - type: string - secretType: - default: Opaque - description: 'The Kubernetes Secret type (experimental feature). More info: https://kubernetes.io/docs/concepts/configuration/secret/#secret-types' - type: string - template: - description: The template to transform the secret data - properties: - data: - additionalProperties: - type: string - description: The template key values - type: object - includeAllSecrets: - description: This injects all retrieved secrets into the top level of your template. Secrets defined in the template will take precedence over the injected ones. - type: boolean - type: object - required: - - secretName - - secretNamespace - type: object - resyncInterval: - default: 60 - type: integer - tls: - properties: - caRef: - description: Reference to secret containing CA cert - properties: - key: - description: The name of the secret property with the CA certificate value - type: string - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The namespace where the Kubernetes Secret is located - type: string - required: - - key - secretName - secretNamespace - type: object - type: object - tokenSecretReference: - properties: - secretName: - description: The name of the Kubernetes Secret - type: string - secretNamespace: - description: The name space where the Kubernetes Secret is located - type: string - required: - - secretName - - secretNamespace - type: object - required: - - resyncInterval - type: object - status: - description: InfisicalSecretStatus defines the observed state of InfisicalSecret - properties: - conditions: - items: - description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + type: object + resyncInterval: + default: 60 + type: integer + tls: properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time + caRef: + description: Reference to secret containing CA cert + properties: + key: + description: The name of the secret property with the CA certificate value + type: string + secretName: + description: The name of the Kubernetes Secret + type: string + secretNamespace: + description: The namespace where the Kubernetes Secret is located + type: string + required: + - key + - secretName + - secretNamespace + type: object + type: object + tokenSecretReference: + properties: + secretName: + description: The name of the Kubernetes Secret type: string - message: - description: message is a human readable message indicating details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + secretNamespace: + description: The name space where the Kubernetes Secret is located type: string required: - - lastTransitionTime - - message - - reason - - status - - type + - secretName + - secretNamespace type: object - type: array - required: - - conditions - type: object - type: object - served: true - storage: true - subresources: - status: {} + required: + - resyncInterval + type: object + status: + description: InfisicalSecretStatus defines the observed state of InfisicalSecret + properties: + conditions: + items: + description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + properties: + lastTransitionTime: + description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: message is a human readable message indicating details about the transition. This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + required: + - conditions + type: object + type: object + served: true + storage: true + subresources: + status: {} --- apiVersion: v1 kind: ServiceAccount @@ -942,37 +942,37 @@ metadata: name: infisical-operator-leader-election-role namespace: infisical-operator-system rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - get - - list - - watch - - create - - update - - patch - - delete -- apiGroups: - - "" - resources: - - events - verbs: - - create - - patch + - apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - "" + resources: + - events + verbs: + - create + - patch --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -980,134 +980,134 @@ metadata: creationTimestamp: null name: infisical-operator-manager-role rules: -- apiGroups: - - "" - resources: - - configmaps - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - "" - resources: - - secrets - verbs: - - create - - delete - - get - - list - - update - - watch -- apiGroups: - - "" - resources: - - serviceaccounts - verbs: - - get - - list - - watch -- apiGroups: - - apps - resources: - - daemonsets - - deployments - - statefulsets - verbs: - - get - - list - - update - - watch -- apiGroups: - - apps - resources: - - deployments - verbs: - - get - - list - - update - - watch -- apiGroups: - - secrets.infisical.com - resources: - - infisicaldynamicsecrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - secrets.infisical.com - resources: - - infisicaldynamicsecrets/finalizers - verbs: - - update -- apiGroups: - - secrets.infisical.com - resources: - - infisicaldynamicsecrets/status - verbs: - - get - - patch - - update -- apiGroups: - - secrets.infisical.com - resources: - - infisicalpushsecrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - secrets.infisical.com - resources: - - infisicalpushsecrets/finalizers - verbs: - - update -- apiGroups: - - secrets.infisical.com - resources: - - infisicalpushsecrets/status - verbs: - - get - - patch - - update -- apiGroups: - - secrets.infisical.com - resources: - - infisicalsecrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - secrets.infisical.com - resources: - - infisicalsecrets/finalizers - verbs: - - update -- apiGroups: - - secrets.infisical.com - resources: - - infisicalsecrets/status - verbs: - - get - - patch - - update + - apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - get + - list + - update + - watch + - apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - update + - watch + - apiGroups: + - "" + resources: + - serviceaccounts + verbs: + - get + - list + - watch + - apiGroups: + - apps + resources: + - daemonsets + - deployments + - statefulsets + verbs: + - get + - list + - update + - watch + - apiGroups: + - apps + resources: + - deployments + verbs: + - get + - list + - update + - watch + - apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets/finalizers + verbs: + - update + - apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets/status + verbs: + - get + - patch + - update + - apiGroups: + - secrets.infisical.com + resources: + - infisicalpushsecrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - secrets.infisical.com + resources: + - infisicalpushsecrets/finalizers + verbs: + - update + - apiGroups: + - secrets.infisical.com + resources: + - infisicalpushsecrets/status + verbs: + - get + - patch + - update + - apiGroups: + - secrets.infisical.com + resources: + - infisicalsecrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - secrets.infisical.com + resources: + - infisicalsecrets/finalizers + verbs: + - update + - apiGroups: + - secrets.infisical.com + resources: + - infisicalsecrets/status + verbs: + - get + - patch + - update --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -1121,10 +1121,10 @@ metadata: app.kubernetes.io/part-of: k8-operator name: infisical-operator-metrics-reader rules: -- nonResourceURLs: - - /metrics - verbs: - - get + - nonResourceURLs: + - /metrics + verbs: + - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -1138,18 +1138,18 @@ metadata: app.kubernetes.io/part-of: k8-operator name: infisical-operator-proxy-role rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding @@ -1168,9 +1168,9 @@ roleRef: kind: Role name: infisical-operator-leader-election-role subjects: -- kind: ServiceAccount - name: infisical-operator-controller-manager - namespace: infisical-operator-system + - kind: ServiceAccount + name: infisical-operator-controller-manager + namespace: infisical-operator-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -1188,9 +1188,9 @@ roleRef: kind: ClusterRole name: infisical-operator-manager-role subjects: -- kind: ServiceAccount - name: infisical-operator-controller-manager - namespace: infisical-operator-system + - kind: ServiceAccount + name: infisical-operator-controller-manager + namespace: infisical-operator-system --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -1208,9 +1208,9 @@ roleRef: kind: ClusterRole name: infisical-operator-proxy-role subjects: -- kind: ServiceAccount - name: infisical-operator-controller-manager - namespace: infisical-operator-system + - kind: ServiceAccount + name: infisical-operator-controller-manager + namespace: infisical-operator-system --- apiVersion: v1 kind: Service @@ -1227,10 +1227,10 @@ metadata: namespace: infisical-operator-system spec: ports: - - name: https - port: 8443 - protocol: TCP - targetPort: https + - name: https + port: 8443 + protocol: TCP + targetPort: https selector: control-plane: controller-manager --- @@ -1263,74 +1263,51 @@ spec: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/arch - operator: In - values: - - amd64 - - arm64 - - ppc64le - - s390x - - key: kubernetes.io/os - operator: In - values: - - linux + - matchExpressions: + - key: kubernetes.io/arch + operator: In + values: + - amd64 + - arm64 + - ppc64le + - s390x + - key: kubernetes.io/os + operator: In + values: + - linux containers: - - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=0 - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.15.0 - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - - args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=127.0.0.1:8080 - - --leader-elect - command: - - /manager - image: infisical/kubernetes-operator:latest - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - name: manager - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + command: + - /manager + image: infisical/kubernetes-operator:latest + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + name: manager + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL securityContext: runAsNonRoot: true serviceAccountName: infisical-operator-controller-manager diff --git a/k8-operator/main.go b/k8-operator/main.go deleted file mode 100644 index 234b05913..000000000 --- a/k8-operator/main.go +++ /dev/null @@ -1,137 +0,0 @@ -package main - -import ( - "flag" - "os" - "time" - - // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) - // to ensure that exec-entrypoint and run can make use of them. - "math/rand" - - _ "k8s.io/client-go/plugin/pkg/client/auth" - - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - clientgoscheme "k8s.io/client-go/kubernetes/scheme" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/healthz" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - - secretsv1alpha1 "github.com/Infisical/infisical/k8-operator/api/v1alpha1" - infisicalDynamicSecretController "github.com/Infisical/infisical/k8-operator/controllers/infisicaldynamicsecret" - infisicalPushSecretController "github.com/Infisical/infisical/k8-operator/controllers/infisicalpushsecret" - infisicalSecretController "github.com/Infisical/infisical/k8-operator/controllers/infisicalsecret" - "github.com/Infisical/infisical/k8-operator/packages/template" - //+kubebuilder:scaffold:imports -) - -var ( - scheme = runtime.NewScheme() - setupLog = ctrl.Log.WithName("setup") -) - -func init() { - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) - - utilruntime.Must(secretsv1alpha1.AddToScheme(scheme)) - //+kubebuilder:scaffold:scheme -} - -func main() { - var metricsAddr string - var enableLeaderElection bool - var probeAddr string - var namespace string - flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") - flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") - flag.StringVar(&namespace, "namespace", "", "Watch InfisicalSecrets scoped in the provided namespace only") - flag.BoolVar(&enableLeaderElection, "leader-elect", false, - "Enable leader election for controller manager. "+ - "Enabling this will ensure there is only one active controller manager.") - opts := zap.Options{ - Development: true, - } - opts.BindFlags(flag.CommandLine) - flag.Parse() - - ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) - - ctrlOpts := ctrl.Options{ - Scheme: scheme, - MetricsBindAddress: metricsAddr, - Port: 9443, - HealthProbeBindAddress: probeAddr, - LeaderElection: enableLeaderElection, - LeaderElectionID: "cf2b8c44.infisical.com", - // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily - // when the Manager ends. This requires the binary to immediately end when the - // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly - // speeds up voluntary leader transitions as the new leader don't have to wait - // LeaseDuration time first. - // - // In the default scaffold provided, the program ends immediately after - // the manager stops, so would be fine to enable this option. However, - // if you are doing or is intended to do any operation such as perform cleanups - // after the manager stops then its usage might be unsafe. - // LeaderElectionReleaseOnCancel: true, - } - - if namespace != "" { - ctrlOpts.Namespace = namespace - } - - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrlOpts) - if err != nil { - setupLog.Error(err, "unable to start manager") - os.Exit(1) - } - - template.InitializeTemplateFunctions() - - if err = (&infisicalSecretController.InfisicalSecretReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - BaseLogger: ctrl.Log, - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "InfisicalSecret") - os.Exit(1) - } - - if err = (&infisicalPushSecretController.InfisicalPushSecretReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - BaseLogger: ctrl.Log, - IsNamespaceScoped: namespace != "", - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "InfisicalPushSecret") - os.Exit(1) - } - - if err = (&infisicalDynamicSecretController.InfisicalDynamicSecretReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - BaseLogger: ctrl.Log, - Random: rand.New(rand.NewSource(time.Now().UnixNano())), - }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "InfisicalDynamicSecret") - os.Exit(1) - } - - //+kubebuilder:scaffold:builder - - if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up health check") - os.Exit(1) - } - if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { - setupLog.Error(err, "unable to set up ready check") - os.Exit(1) - } - - setupLog.Info("starting manager") - if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { - setupLog.Error(err, "problem running manager") - os.Exit(1) - } -} diff --git a/k8-operator/packages/api/api.go b/k8-operator/packages/api/api.go deleted file mode 100644 index 36edfa5c1..000000000 --- a/k8-operator/packages/api/api.go +++ /dev/null @@ -1,148 +0,0 @@ -package api - -import ( - "fmt" - - "github.com/go-resty/resty/v2" -) - -const USER_AGENT_NAME = "k8-operator" - -func CallGetServiceTokenDetailsV2(httpClient *resty.Client) (GetServiceTokenDetailsResponse, error) { - var tokenDetailsResponse GetServiceTokenDetailsResponse - response, err := httpClient. - R(). - SetResult(&tokenDetailsResponse). - SetHeader("User-Agent", USER_AGENT_NAME). - Get(fmt.Sprintf("%v/v2/service-token", API_HOST_URL)) - - if err != nil { - return GetServiceTokenDetailsResponse{}, fmt.Errorf("CallGetServiceTokenDetails: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return GetServiceTokenDetailsResponse{}, fmt.Errorf("CallGetServiceTokenDetails: Unsuccessful response: [response=%s]", response) - } - - return tokenDetailsResponse, nil -} - -func CallGetServiceTokenAccountDetailsV2(httpClient *resty.Client) (ServiceAccountDetailsResponse, error) { - var serviceAccountDetailsResponse ServiceAccountDetailsResponse - response, err := httpClient. - R(). - SetResult(&serviceAccountDetailsResponse). - SetHeader("User-Agent", USER_AGENT_NAME). - Get(fmt.Sprintf("%v/v2/service-accounts/me", API_HOST_URL)) - - if err != nil { - return ServiceAccountDetailsResponse{}, fmt.Errorf("CallGetServiceTokenAccountDetailsV2: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return ServiceAccountDetailsResponse{}, fmt.Errorf("CallGetServiceTokenAccountDetailsV2: Unsuccessful response: [response=%s]", response) - } - - return serviceAccountDetailsResponse, nil -} - -func CallUniversalMachineIdentityLogin(request MachineIdentityUniversalAuthLoginRequest) (MachineIdentityDetailsResponse, error) { - var machineIdentityDetailsResponse MachineIdentityDetailsResponse - - response, err := resty.New(). - R(). - SetResult(&machineIdentityDetailsResponse). - SetBody(request). - SetHeader("User-Agent", USER_AGENT_NAME). - Post(fmt.Sprintf("%v/v1/auth/universal-auth/login", API_HOST_URL)) - - if err != nil { - return MachineIdentityDetailsResponse{}, fmt.Errorf("CallUniversalMachineIdentityLogin: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return MachineIdentityDetailsResponse{}, fmt.Errorf("CallUniversalMachineIdentityLogin: Unsuccessful response: [response=%s]", response) - } - - return machineIdentityDetailsResponse, nil -} - -func CallUniversalMachineIdentityRefreshAccessToken(request MachineIdentityUniversalAuthRefreshRequest) (MachineIdentityDetailsResponse, error) { - var universalAuthRefreshResponse MachineIdentityDetailsResponse - - response, err := resty.New(). - R(). - SetResult(&universalAuthRefreshResponse). - SetHeader("User-Agent", USER_AGENT_NAME). - SetBody(request). - Post(fmt.Sprintf("%v/v1/auth/token/renew", API_HOST_URL)) - - if err != nil { - return MachineIdentityDetailsResponse{}, fmt.Errorf("CallUniversalAuthRefreshAccessToken: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return MachineIdentityDetailsResponse{}, fmt.Errorf("CallUniversalAuthRefreshAccessToken: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String()) - } - - return universalAuthRefreshResponse, nil -} - -func CallGetServiceAccountWorkspacePermissionsV2(httpClient *resty.Client) (ServiceAccountWorkspacePermissions, error) { - var serviceAccountWorkspacePermissionsResponse ServiceAccountWorkspacePermissions - response, err := httpClient. - R(). - SetResult(&serviceAccountWorkspacePermissionsResponse). - SetHeader("User-Agent", USER_AGENT_NAME). - Get(fmt.Sprintf("%v/v2/service-accounts//permissions/workspace", API_HOST_URL)) - - if err != nil { - return ServiceAccountWorkspacePermissions{}, fmt.Errorf("CallGetServiceAccountWorkspacePermissionsV2: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return ServiceAccountWorkspacePermissions{}, fmt.Errorf("CallGetServiceAccountWorkspacePermissionsV2: Unsuccessful response: [response=%s]", response) - } - - return serviceAccountWorkspacePermissionsResponse, nil -} - -func CallGetServiceAccountKeysV2(httpClient *resty.Client, request GetServiceAccountKeysRequest) (GetServiceAccountKeysResponse, error) { - var serviceAccountKeysResponse GetServiceAccountKeysResponse - response, err := httpClient. - R(). - SetResult(&serviceAccountKeysResponse). - SetHeader("User-Agent", USER_AGENT_NAME). - Get(fmt.Sprintf("%v/v2/service-accounts/%v/keys", API_HOST_URL, request.ServiceAccountId)) - - if err != nil { - return GetServiceAccountKeysResponse{}, fmt.Errorf("CallGetServiceAccountKeysV2: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return GetServiceAccountKeysResponse{}, fmt.Errorf("CallGetServiceAccountKeysV2: Unsuccessful response: [response=%s]", response) - } - - return serviceAccountKeysResponse, nil -} - -func CallGetProjectByID(httpClient *resty.Client, request GetProjectByIDRequest) (GetProjectByIDResponse, error) { - - var projectResponse GetProjectByIDResponse - - response, err := httpClient. - R().SetResult(&projectResponse). - SetHeader("User-Agent", USER_AGENT_NAME). - Get(fmt.Sprintf("%s/v1/workspace/%s", API_HOST_URL, request.ProjectID)) - - if err != nil { - return GetProjectByIDResponse{}, fmt.Errorf("CallGetProject: Unable to complete api request [err=%s]", err) - } - - if response.IsError() { - return GetProjectByIDResponse{}, fmt.Errorf("CallGetProject: Unsuccessful response: [response=%s]", response) - } - - return projectResponse, nil - -} diff --git a/k8-operator/packages/api/models.go b/k8-operator/packages/api/models.go deleted file mode 100644 index 01f835397..000000000 --- a/k8-operator/packages/api/models.go +++ /dev/null @@ -1,208 +0,0 @@ -package api - -import ( - "time" - - "github.com/Infisical/infisical/k8-operator/packages/model" -) - -type GetEncryptedWorkspaceKeyRequest struct { - WorkspaceId string `json:"workspaceId"` -} - -type GetEncryptedWorkspaceKeyResponse struct { - ID string `json:"_id"` - EncryptedKey string `json:"encryptedKey"` - Nonce string `json:"nonce"` - Sender struct { - ID string `json:"_id"` - Email string `json:"email"` - RefreshVersion int `json:"refreshVersion"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - V int `json:"__v"` - FirstName string `json:"firstName"` - LastName string `json:"lastName"` - PublicKey string `json:"publicKey"` - } `json:"sender"` - Receiver string `json:"receiver"` - Workspace string `json:"workspace"` - V int `json:"__v"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` -} - -type GetEncryptedSecretsV3Request struct { - Environment string `json:"environment"` - WorkspaceId string `json:"workspaceId"` - Recursive bool `json:"recursive"` - SecretPath string `json:"secretPath"` - IncludeImport bool `json:"include_imports"` - ETag string `json:"etag,omitempty"` -} - -type EncryptedSecretV3 struct { - ID string `json:"_id"` - Version int `json:"version"` - Workspace string `json:"workspace"` - Type string `json:"type"` - Tags []struct { - ID string `json:"_id"` - Name string `json:"name"` - Slug string `json:"slug"` - Workspace string `json:"workspace"` - } `json:"tags"` - Environment string `json:"environment"` - SecretKeyCiphertext string `json:"secretKeyCiphertext"` - SecretKeyIV string `json:"secretKeyIV"` - SecretKeyTag string `json:"secretKeyTag"` - SecretValueCiphertext string `json:"secretValueCiphertext"` - SecretValueIV string `json:"secretValueIV"` - SecretValueTag string `json:"secretValueTag"` - SecretCommentCiphertext string `json:"secretCommentCiphertext"` - SecretCommentIV string `json:"secretCommentIV"` - SecretCommentTag string `json:"secretCommentTag"` - Algorithm string `json:"algorithm"` - KeyEncoding string `json:"keyEncoding"` - Folder string `json:"folder"` - V int `json:"__v"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` -} - -type DecryptedSecretV3 struct { - ID string `json:"id"` - Workspace string `json:"workspace"` - Environment string `json:"environment"` - Version int `json:"version"` - Type string `json:"string"` - SecretKey string `json:"secretKey"` - SecretValue string `json:"secretValue"` - SecretComment string `json:"secretComment"` -} - -type ImportedSecretV3 struct { - Environment string `json:"environment"` - FolderId string `json:"folderId"` - SecretPath string `json:"secretPath"` - Secrets []EncryptedSecretV3 `json:"secrets"` -} - -type ImportedRawSecretV3 struct { - Environment string `json:"environment"` - FolderId string `json:"folderId"` - SecretPath string `json:"secretPath"` - Secrets []DecryptedSecretV3 `json:"secrets"` -} - -type GetEncryptedSecretsV3Response struct { - Secrets []EncryptedSecretV3 `json:"secrets"` - ImportedSecrets []ImportedSecretV3 `json:"imports,omitempty"` - Modified bool `json:"modified,omitempty"` - ETag string `json:"ETag,omitempty"` -} - -type GetDecryptedSecretsV3Response struct { - Secrets []DecryptedSecretV3 `json:"secrets"` - ETag string `json:"ETag,omitempty"` - Modified bool `json:"modified,omitempty"` - Imports []ImportedRawSecretV3 `json:"imports,omitempty"` -} - -type GetDecryptedSecretsV3Request struct { - ProjectID string `json:"workspaceId"` - ProjectSlug string `json:"workspaceSlug"` - Environment string `json:"environment"` - SecretPath string `json:"secretPath"` - Recursive bool `json:"recursive"` - ExpandSecretReferences bool `json:"expandSecretReferences"` - ETag string `json:"etag,omitempty"` -} - -type GetServiceTokenDetailsResponse struct { - ID string `json:"_id"` - Name string `json:"name"` - Workspace string `json:"workspace"` - Environment string `json:"environment"` - EncryptedKey string `json:"encryptedKey"` - Iv string `json:"iv"` - Tag string `json:"tag"` - SecretPath string `json:"secretPath"` -} - -type ServiceAccountDetailsResponse struct { - ServiceAccount struct { - ID string `json:"_id"` - Name string `json:"name"` - Organization string `json:"organization"` - PublicKey string `json:"publicKey"` - LastUsed time.Time `json:"lastUsed"` - ExpiresAt time.Time `json:"expiresAt"` - } `json:"serviceAccount"` -} - -type MachineIdentityDetailsResponse struct { - AccessToken string `json:"accessToken"` - ExpiresIn int `json:"expiresIn"` - AccessTokenMaxTTL int `json:"accessTokenMaxTTL"` - TokenType string `json:"tokenType"` -} - -type ServiceAccountWorkspacePermission struct { - ID string `json:"_id"` - ServiceAccount string `json:"serviceAccount"` - Workspace struct { - ID string `json:"_id"` - Name string `json:"name"` - AutoCapitalization bool `json:"autoCapitalization"` - Organization string `json:"organization"` - Environments []struct { - Name string `json:"name"` - Slug string `json:"slug"` - ID string `json:"_id"` - } `json:"environments"` - } `json:"workspace"` - Environment string `json:"environment"` - Read bool `json:"read"` - Write bool `json:"write"` -} - -type ServiceAccountWorkspacePermissions struct { - ServiceAccountWorkspacePermission []ServiceAccountWorkspacePermissions `json:"serviceAccountWorkspacePermissions"` -} - -type GetServiceAccountKeysRequest struct { - ServiceAccountId string `json:"id"` -} - -type MachineIdentityUniversalAuthLoginRequest struct { - ClientId string `json:"clientId"` - ClientSecret string `json:"clientSecret"` -} - -type MachineIdentityUniversalAuthRefreshRequest struct { - AccessToken string `json:"accessToken"` -} - -type ServiceAccountKey struct { - ID string `json:"_id"` - EncryptedKey string `json:"encryptedKey"` - Nonce string `json:"nonce"` - Sender string `json:"sender"` - ServiceAccount string `json:"serviceAccount"` - Workspace string `json:"workspace"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` -} - -type GetServiceAccountKeysResponse struct { - ServiceAccountKeys []ServiceAccountKey `json:"serviceAccountKeys"` -} - -type GetProjectByIDRequest struct { - ProjectID string -} - -type GetProjectByIDResponse struct { - Project model.Project `json:"workspace"` -} diff --git a/k8-operator/packages/api/variables.go b/k8-operator/packages/api/variables.go deleted file mode 100644 index 1dd255d42..000000000 --- a/k8-operator/packages/api/variables.go +++ /dev/null @@ -1,4 +0,0 @@ -package api - -var API_HOST_URL string = "https://app.infisical.com/api" -var API_CA_CERTIFICATE string = "" diff --git a/k8-operator/packages/constants/constants.go b/k8-operator/packages/constants/constants.go deleted file mode 100644 index e5e2d8ff8..000000000 --- a/k8-operator/packages/constants/constants.go +++ /dev/null @@ -1,42 +0,0 @@ -package constants - -import "errors" - -const SERVICE_ACCOUNT_ACCESS_KEY = "serviceAccountAccessKey" -const SERVICE_ACCOUNT_PUBLIC_KEY = "serviceAccountPublicKey" -const SERVICE_ACCOUNT_PRIVATE_KEY = "serviceAccountPrivateKey" - -const INFISICAL_MACHINE_IDENTITY_CLIENT_ID = "clientId" -const INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET = "clientSecret" - -const INFISICAL_TOKEN_SECRET_KEY_NAME = "infisicalToken" -const SECRET_VERSION_ANNOTATION = "secrets.infisical.com/version" // used to set the version of secrets via Etag -const OPERATOR_SETTINGS_CONFIGMAP_NAME = "infisical-config" -const OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE = "infisical-operator-system" -const INFISICAL_DOMAIN = "https://app.infisical.com/api" - -const INFISICAL_PUSH_SECRET_FINALIZER_NAME = "pushsecret.secrets.infisical.com/finalizer" -const INFISICAL_DYNAMIC_SECRET_FINALIZER_NAME = "dynamicsecret.secrets.infisical.com/finalizer" - -type PushSecretReplacePolicy string -type PushSecretDeletionPolicy string - -const ( - PUSH_SECRET_REPLACE_POLICY_ENABLED PushSecretReplacePolicy = "Replace" - PUSH_SECRET_DELETE_POLICY_ENABLED PushSecretDeletionPolicy = "Delete" -) - -type ManagedKubeResourceType string - -const ( - MANAGED_KUBE_RESOURCE_TYPE_SECRET ManagedKubeResourceType = "Secret" - MANAGED_KUBE_RESOURCE_TYPE_CONFIG_MAP ManagedKubeResourceType = "ConfigMap" -) - -type DynamicSecretLeaseRevocationPolicy string - -const ( - DYNAMIC_SECRET_LEASE_REVOCATION_POLICY_ENABLED DynamicSecretLeaseRevocationPolicy = "Revoke" -) - -var ErrInvalidLease = errors.New("invalid dynamic secret lease") diff --git a/k8-operator/packages/controllerhelpers/controllerhelpers.go b/k8-operator/packages/controllerhelpers/controllerhelpers.go deleted file mode 100644 index c08a085a1..000000000 --- a/k8-operator/packages/controllerhelpers/controllerhelpers.go +++ /dev/null @@ -1,293 +0,0 @@ -package controllerhelpers - -import ( - "context" - "fmt" - "sync" - - "github.com/Infisical/infisical/k8-operator/api/v1alpha1" - "github.com/Infisical/infisical/k8-operator/packages/constants" - "github.com/go-logr/logr" - v1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - k8Errors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" - controllerClient "sigs.k8s.io/controller-runtime/pkg/client" -) - -const DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX = "secrets.infisical.com/managed-secret" -const AUTO_RELOAD_DEPLOYMENT_ANNOTATION = "secrets.infisical.com/auto-reload" // needs to be set to true for a deployment to start auto redeploying - -func ReconcileDeploymentsWithManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecret v1alpha1.ManagedKubeSecretConfig) (int, error) { - listOfDeployments := &v1.DeploymentList{} - - err := client.List(ctx, listOfDeployments, &controllerClient.ListOptions{Namespace: managedSecret.SecretNamespace}) - if err != nil { - return 0, fmt.Errorf("unable to get deployments in the [namespace=%v] [err=%v]", managedSecret.SecretNamespace, err) - } - - listOfDaemonSets := &v1.DaemonSetList{} - err = client.List(ctx, listOfDaemonSets, &controllerClient.ListOptions{Namespace: managedSecret.SecretNamespace}) - if err != nil { - return 0, fmt.Errorf("unable to get daemonSets in the [namespace=%v] [err=%v]", managedSecret.SecretNamespace, err) - } - - listOfStatefulSets := &v1.StatefulSetList{} - err = client.List(ctx, listOfStatefulSets, &controllerClient.ListOptions{Namespace: managedSecret.SecretNamespace}) - if err != nil { - return 0, fmt.Errorf("unable to get statefulSets in the [namespace=%v] [err=%v]", managedSecret.SecretNamespace, err) - } - - managedKubeSecretNameAndNamespace := types.NamespacedName{ - Namespace: managedSecret.SecretNamespace, - Name: managedSecret.SecretName, - } - - managedKubeSecret := &corev1.Secret{} - err = client.Get(ctx, managedKubeSecretNameAndNamespace, managedKubeSecret) - if err != nil { - return 0, fmt.Errorf("unable to fetch Kubernetes secret to update deployment: %v", err) - } - - var wg sync.WaitGroup - - // Iterate over the deployments and check if they use the managed secret - for _, deployment := range listOfDeployments.Items { - deployment := deployment - if deployment.Annotations[AUTO_RELOAD_DEPLOYMENT_ANNOTATION] == "true" && IsDeploymentUsingManagedSecret(deployment, managedSecret) { - // Start a goroutine to reconcile the deployment - wg.Add(1) - go func(deployment v1.Deployment, managedSecret corev1.Secret) { - defer wg.Done() - if err := ReconcileDeployment(ctx, client, logger, deployment, managedSecret); err != nil { - logger.Error(err, fmt.Sprintf("unable to reconcile deployment with [name=%v]. Will try next requeue", deployment.ObjectMeta.Name)) - } - }(deployment, *managedKubeSecret) - } - } - - // Iterate over the daemonSets and check if they use the managed secret - for _, daemonSet := range listOfDaemonSets.Items { - daemonSet := daemonSet - if daemonSet.Annotations[AUTO_RELOAD_DEPLOYMENT_ANNOTATION] == "true" && IsDaemonSetUsingManagedSecret(daemonSet, managedSecret) { - wg.Add(1) - go func(deployment v1.DaemonSet, managedSecret corev1.Secret) { - defer wg.Done() - if err := ReconcileDaemonSet(ctx, client, logger, daemonSet, managedSecret); err != nil { - logger.Error(err, fmt.Sprintf("unable to reconcile daemonset with [name=%v]. Will try next requeue", deployment.ObjectMeta.Name)) - } - }(daemonSet, *managedKubeSecret) - } - } - - // Iterate over the statefulSets and check if they use the managed secret - for _, statefulSet := range listOfStatefulSets.Items { - statefulSet := statefulSet - if statefulSet.Annotations[AUTO_RELOAD_DEPLOYMENT_ANNOTATION] == "true" && IsStatefulSetUsingManagedSecret(statefulSet, managedSecret) { - wg.Add(1) - go func(statefulSet v1.StatefulSet, managedSecret corev1.Secret) { - defer wg.Done() - if err := ReconcileStatefulSet(ctx, client, logger, statefulSet, managedSecret); err != nil { - logger.Error(err, fmt.Sprintf("unable to reconcile statefulset with [name=%v]. Will try next requeue", statefulSet.ObjectMeta.Name)) - } - }(statefulSet, *managedKubeSecret) - } - } - - wg.Wait() - - return 0, nil -} - -func ReconcileDeploymentsWithMultipleManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecrets []v1alpha1.ManagedKubeSecretConfig) (int, error) { - for _, managedSecret := range managedSecrets { - _, err := ReconcileDeploymentsWithManagedSecrets(ctx, client, logger, managedSecret) - if err != nil { - logger.Error(err, fmt.Sprintf("unable to reconcile deployments with managed secret [name=%v]", managedSecret.SecretName)) - return 0, err - } - } - return 0, nil -} - -// Check if the deployment uses managed secrets -func IsDeploymentUsingManagedSecret(deployment v1.Deployment, managedSecret v1alpha1.ManagedKubeSecretConfig) bool { - managedSecretName := managedSecret.SecretName - for _, container := range deployment.Spec.Template.Spec.Containers { - for _, envFrom := range container.EnvFrom { - if envFrom.SecretRef != nil && envFrom.SecretRef.LocalObjectReference.Name == managedSecretName { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.LocalObjectReference.Name == managedSecretName { - return true - } - } - } - for _, volume := range deployment.Spec.Template.Spec.Volumes { - if volume.Secret != nil && volume.Secret.SecretName == managedSecretName { - return true - } - } - - return false -} - -func IsDaemonSetUsingManagedSecret(daemonSet v1.DaemonSet, managedSecret v1alpha1.ManagedKubeSecretConfig) bool { - managedSecretName := managedSecret.SecretName - for _, container := range daemonSet.Spec.Template.Spec.Containers { - for _, envFrom := range container.EnvFrom { - if envFrom.SecretRef != nil && envFrom.SecretRef.LocalObjectReference.Name == managedSecretName { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.LocalObjectReference.Name == managedSecretName { - return true - } - } - } - - for _, volume := range daemonSet.Spec.Template.Spec.Volumes { - if volume.Secret != nil && volume.Secret.SecretName == managedSecretName { - return true - } - } - - return false -} - -func IsStatefulSetUsingManagedSecret(statefulSet v1.StatefulSet, managedSecret v1alpha1.ManagedKubeSecretConfig) bool { - managedSecretName := managedSecret.SecretName - for _, container := range statefulSet.Spec.Template.Spec.Containers { - for _, envFrom := range container.EnvFrom { - if envFrom.SecretRef != nil && envFrom.SecretRef.LocalObjectReference.Name == managedSecretName { - return true - } - } - for _, env := range container.Env { - if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil && env.ValueFrom.SecretKeyRef.LocalObjectReference.Name == managedSecretName { - return true - } - } - } - for _, volume := range statefulSet.Spec.Template.Spec.Volumes { - if volume.Secret != nil && volume.Secret.SecretName == managedSecretName { - return true - } - } - - return false -} - -// This function ensures that a deployment is in sync with a Kubernetes secret by comparing their versions. -// If the version of the secret is different from the version annotation on the deployment, the annotation is updated to trigger a restart of the deployment. -func ReconcileDeployment(ctx context.Context, client controllerClient.Client, logger logr.Logger, deployment v1.Deployment, secret corev1.Secret) error { - annotationKey := fmt.Sprintf("%s.%s", DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX, secret.Name) - annotationValue := secret.Annotations[constants.SECRET_VERSION_ANNOTATION] - - if deployment.Annotations[annotationKey] == annotationValue && - deployment.Spec.Template.Annotations[annotationKey] == annotationValue { - logger.Info(fmt.Sprintf("The [deploymentName=%v] is already using the most up to date managed secrets. No action required.", deployment.ObjectMeta.Name)) - return nil - } - - logger.Info(fmt.Sprintf("Deployment is using outdated managed secret. Starting re-deployment [deploymentName=%v]", deployment.ObjectMeta.Name)) - - if deployment.Spec.Template.Annotations == nil { - deployment.Spec.Template.Annotations = make(map[string]string) - } - - deployment.Annotations[annotationKey] = annotationValue - deployment.Spec.Template.Annotations[annotationKey] = annotationValue - - if err := client.Update(ctx, &deployment); err != nil { - return fmt.Errorf("failed to update deployment annotation: %v", err) - } - return nil -} - -func ReconcileDaemonSet(ctx context.Context, client controllerClient.Client, logger logr.Logger, daemonSet v1.DaemonSet, secret corev1.Secret) error { - annotationKey := fmt.Sprintf("%s.%s", DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX, secret.Name) - annotationValue := secret.Annotations[constants.SECRET_VERSION_ANNOTATION] - - if daemonSet.Annotations[annotationKey] == annotationValue && - daemonSet.Spec.Template.Annotations[annotationKey] == annotationValue { - logger.Info(fmt.Sprintf("The [daemonSetName=%v] is already using the most up to date managed secrets. No action required.", daemonSet.ObjectMeta.Name)) - return nil - } - - logger.Info(fmt.Sprintf("DaemonSet is using outdated managed secret. Starting re-deployment [daemonSetName=%v]", daemonSet.ObjectMeta.Name)) - - if daemonSet.Spec.Template.Annotations == nil { - daemonSet.Spec.Template.Annotations = make(map[string]string) - } - - daemonSet.Annotations[annotationKey] = annotationValue - daemonSet.Spec.Template.Annotations[annotationKey] = annotationValue - - if err := client.Update(ctx, &daemonSet); err != nil { - return fmt.Errorf("failed to update daemonSet annotation: %v", err) - } - return nil -} - -func ReconcileStatefulSet(ctx context.Context, client controllerClient.Client, logger logr.Logger, statefulSet v1.StatefulSet, secret corev1.Secret) error { - annotationKey := fmt.Sprintf("%s.%s", DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX, secret.Name) - annotationValue := secret.Annotations[constants.SECRET_VERSION_ANNOTATION] - - if statefulSet.Annotations[annotationKey] == annotationValue && - statefulSet.Spec.Template.Annotations[annotationKey] == annotationValue { - logger.Info(fmt.Sprintf("The [statefulSetName=%v] is already using the most up to date managed secrets. No action required.", statefulSet.ObjectMeta.Name)) - return nil - } - - logger.Info(fmt.Sprintf("StatefulSet is using outdated managed secret. Starting re-deployment [statefulSetName=%v]", statefulSet.ObjectMeta.Name)) - - if statefulSet.Spec.Template.Annotations == nil { - statefulSet.Spec.Template.Annotations = make(map[string]string) - } - - statefulSet.Annotations[annotationKey] = annotationValue - statefulSet.Spec.Template.Annotations[annotationKey] = annotationValue - - if err := client.Update(ctx, &statefulSet); err != nil { - return fmt.Errorf("failed to update statefulSet annotation: %v", err) - } - return nil -} - -func GetInfisicalConfigMap(ctx context.Context, client client.Client) (configMap map[string]string, errToReturn error) { - // default key values - defaultConfigMapData := make(map[string]string) - defaultConfigMapData["hostAPI"] = constants.INFISICAL_DOMAIN - - kubeConfigMap := &corev1.ConfigMap{} - err := client.Get(ctx, types.NamespacedName{ - Namespace: constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, - Name: constants.OPERATOR_SETTINGS_CONFIGMAP_NAME, - }, kubeConfigMap) - - if err != nil { - if k8Errors.IsNotFound(err) { - kubeConfigMap = nil - } else { - return nil, fmt.Errorf("GetConfigMapByNamespacedName: unable to fetch config map in [namespacedName=%s] [err=%s]", constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, err) - } - } - - if kubeConfigMap == nil { - return defaultConfigMapData, nil - } else { - for key, value := range defaultConfigMapData { - _, exists := kubeConfigMap.Data[key] - if !exists { - kubeConfigMap.Data[key] = value - } - } - - return kubeConfigMap.Data, nil - } -} diff --git a/k8-operator/packages/controllerutil/util.go b/k8-operator/packages/controllerutil/util.go deleted file mode 100644 index 8c610e2e5..000000000 --- a/k8-operator/packages/controllerutil/util.go +++ /dev/null @@ -1,45 +0,0 @@ -package controllerhelpers - -import ( - "context" - "fmt" - - "github.com/Infisical/infisical/k8-operator/packages/constants" - corev1 "k8s.io/api/core/v1" - k8Errors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func GetInfisicalConfigMap(ctx context.Context, client client.Client) (configMap map[string]string, errToReturn error) { - // default key values - defaultConfigMapData := make(map[string]string) - defaultConfigMapData["hostAPI"] = constants.INFISICAL_DOMAIN - - kubeConfigMap := &corev1.ConfigMap{} - err := client.Get(ctx, types.NamespacedName{ - Namespace: constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, - Name: constants.OPERATOR_SETTINGS_CONFIGMAP_NAME, - }, kubeConfigMap) - - if err != nil { - if k8Errors.IsNotFound(err) { - kubeConfigMap = nil - } else { - return nil, fmt.Errorf("GetConfigMapByNamespacedName: unable to fetch config map in [namespacedName=%s] [err=%s]", constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, err) - } - } - - if kubeConfigMap == nil { - return defaultConfigMapData, nil - } else { - for key, value := range defaultConfigMapData { - _, exists := kubeConfigMap.Data[key] - if !exists { - kubeConfigMap.Data[key] = value - } - } - - return kubeConfigMap.Data, nil - } -} diff --git a/k8-operator/packages/crypto/crypto.go b/k8-operator/packages/crypto/crypto.go deleted file mode 100644 index 810382af1..000000000 --- a/k8-operator/packages/crypto/crypto.go +++ /dev/null @@ -1,42 +0,0 @@ -package crypto - -import ( - "crypto/aes" - "crypto/cipher" - "fmt" - "hash/crc32" - - "golang.org/x/crypto/nacl/box" -) - -func DecryptSymmetric(key []byte, encryptedPrivateKey []byte, tag []byte, IV []byte) ([]byte, error) { - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - - aesgcm, err := cipher.NewGCMWithNonceSize(block, len(IV)) - if err != nil { - return nil, err - } - - var nonce = IV - var ciphertext = append(encryptedPrivateKey, tag...) - - plaintext, err := aesgcm.Open(nil, nonce, ciphertext, nil) - if err != nil { - return nil, err - } - - return plaintext, nil -} - -func DecryptAsymmetric(ciphertext []byte, nonce []byte, publicKey []byte, privateKey []byte) (plainText []byte) { - plainTextToReturn, _ := box.Open(nil, ciphertext, (*[24]byte)(nonce), (*[32]byte)(publicKey), (*[32]byte)(privateKey)) - return plainTextToReturn -} - -func ComputeEtag(data []byte) string { - crc := crc32.ChecksumIEEE(data) - return fmt.Sprintf(`W/"secrets-%d-%08X"`, len(data), crc) -} diff --git a/k8-operator/packages/generator/generator.go b/k8-operator/packages/generator/generator.go deleted file mode 100644 index cc1b290c7..000000000 --- a/k8-operator/packages/generator/generator.go +++ /dev/null @@ -1 +0,0 @@ -package generator diff --git a/k8-operator/packages/generator/password.go b/k8-operator/packages/generator/password.go deleted file mode 100644 index d322f1014..000000000 --- a/k8-operator/packages/generator/password.go +++ /dev/null @@ -1,76 +0,0 @@ -package generator - -import ( - "github.com/Infisical/infisical/k8-operator/api/v1alpha1" - "github.com/sethvargo/go-password/password" -) - -const ( - defaultLength = 24 - defaultSymbolChars = "~!@#$%^&*()_+`-={}|[]\\:\"<>?,./" - digitFactor = 0.25 - symbolFactor = 0.25 -) - -func generateSafePassword( - passLen int, - symbols int, - symbolCharacters string, - digits int, - noUpper bool, - allowRepeat bool, -) (string, error) { - gen, err := password.NewGenerator(&password.GeneratorInput{ - Symbols: symbolCharacters, - }) - if err != nil { - return "", err - } - return gen.Generate( - passLen, - digits, - symbols, - noUpper, - allowRepeat, - ) -} - -func GeneratorPassword(spec v1alpha1.PasswordSpec) (string, error) { - - symbolCharacters := defaultSymbolChars - - if spec.SymbolCharacters != nil && *spec.SymbolCharacters != "" { - symbolCharacters = *spec.SymbolCharacters - } - - passwordLength := defaultLength - - if spec.Length != 0 { - passwordLength = spec.Length - } - - digits := int(float32(passwordLength) * digitFactor) - if spec.Digits != nil { - digits = *spec.Digits - } - - symbols := int(float32(passwordLength) * symbolFactor) - if spec.Symbols != nil { - symbols = *spec.Symbols - } - - pass, err := generateSafePassword( - passwordLength, - symbols, - symbolCharacters, - digits, - spec.NoUpper, - spec.AllowRepeat, - ) - - if err != nil { - return "", err - } - - return pass, nil -} diff --git a/k8-operator/packages/generator/uuid.go b/k8-operator/packages/generator/uuid.go deleted file mode 100644 index b9249f783..000000000 --- a/k8-operator/packages/generator/uuid.go +++ /dev/null @@ -1,10 +0,0 @@ -package generator - -import ( - "github.com/google/uuid" -) - -func GeneratorUUID() (string, error) { - uuid := uuid.New().String() - return uuid, nil -} diff --git a/k8-operator/packages/template/base64.go b/k8-operator/packages/template/base64.go deleted file mode 100644 index 3fff06c86..000000000 --- a/k8-operator/packages/template/base64.go +++ /dev/null @@ -1,18 +0,0 @@ -package template - -import ( - "encoding/base64" - "fmt" -) - -func decodeBase64ToBytes(encodedString string) string { - decoded, err := base64.StdEncoding.DecodeString(encodedString) - if err != nil { - panic(fmt.Sprintf("Error: %v", err)) - } - return string(decoded) -} - -func encodeBase64(plainString string) string { - return base64.StdEncoding.EncodeToString([]byte(plainString)) -} diff --git a/k8-operator/packages/template/jwk.go b/k8-operator/packages/template/jwk.go deleted file mode 100644 index 8dbc8f379..000000000 --- a/k8-operator/packages/template/jwk.go +++ /dev/null @@ -1,43 +0,0 @@ -package template - -import ( - "crypto/x509" - "fmt" - - "github.com/lestrrat-go/jwx/v2/jwk" -) - -func jwkPublicKeyPem(jwkjson string) string { - k, err := jwk.ParseKey([]byte(jwkjson)) - if err != nil { - panic(fmt.Sprintf("[jwkPublicKeyPem] Error: %v", err)) - } - var rawkey any - err = k.Raw(&rawkey) - if err != nil { - panic(fmt.Sprintf("[jwkPublicKeyPem] Error: %v", err)) - } - mpk, err := x509.MarshalPKIXPublicKey(rawkey) - if err != nil { - panic(fmt.Sprintf("[jwkPublicKeyPem] Error: %v", err)) - } - return pemEncode(mpk, "PUBLIC KEY") -} - -func jwkPrivateKeyPem(jwkjson string) string { - k, err := jwk.ParseKey([]byte(jwkjson)) - if err != nil { - panic(fmt.Sprintf("[jwkPrivateKeyPem] Error: %v", err)) - } - var mpk []byte - var pk any - err = k.Raw(&pk) - if err != nil { - panic(fmt.Sprintf("[jwkPrivateKeyPem] Error: %v", err)) - } - mpk, err = x509.MarshalPKCS8PrivateKey(pk) - if err != nil { - panic(fmt.Sprintf("[jwkPrivateKeyPem] Error: %v", err)) - } - return pemEncode(mpk, "PRIVATE KEY") -} diff --git a/k8-operator/packages/template/pem.go b/k8-operator/packages/template/pem.go deleted file mode 100644 index f37a9d576..000000000 --- a/k8-operator/packages/template/pem.go +++ /dev/null @@ -1,98 +0,0 @@ -package template - -import ( - "bytes" - "crypto/x509" - "encoding/pem" - "fmt" - "strings" -) - -const ( - errJunk = "error filtering pem: found junk" - - certTypeLeaf = "leaf" - certTypeIntermediate = "intermediate" - certTypeRoot = "root" -) - -func filterPEM(pemType, input string) string { - data := []byte(input) - var blocks []byte - var block *pem.Block - var rest []byte - for { - block, rest = pem.Decode(data) - data = rest - - if block == nil { - break - } - if !strings.EqualFold(block.Type, pemType) { - continue - } - - var buf bytes.Buffer - err := pem.Encode(&buf, block) - if err != nil { - panic(fmt.Sprintf("[filterPEM] Error: %v", err)) - } - blocks = append(blocks, buf.Bytes()...) - } - - if len(blocks) == 0 && len(rest) != 0 { - panic(fmt.Sprintf("[filterPEM] Error: %v", errJunk)) - } - - return string(blocks) -} - -func filterCertChain(certType, input string) string { - ordered := fetchX509CertChains([]byte(input)) - - switch certType { - case certTypeLeaf: - cert := ordered[0] - if cert.AuthorityKeyId != nil && !bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId) { - return pemEncode(ordered[0].Raw, pemTypeCertificate) - } - case certTypeIntermediate: - if len(ordered) < 2 { - return "" - } - var pemData []byte - for _, cert := range ordered[1:] { - if isRootCertificate(cert) { - break - } - b := &pem.Block{ - Type: pemTypeCertificate, - Bytes: cert.Raw, - } - pemData = append(pemData, pem.EncodeToMemory(b)...) - } - return string(pemData) - case certTypeRoot: - cert := ordered[len(ordered)-1] - if isRootCertificate(cert) { - return pemEncode(cert.Raw, pemTypeCertificate) - } - } - - return "" -} - -func isRootCertificate(cert *x509.Certificate) bool { - return cert.AuthorityKeyId == nil || bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId) -} - -func pemEncode(thing []byte, kind string) string { - buf := bytes.NewBuffer(nil) - err := pem.Encode(buf, &pem.Block{Type: kind, Bytes: thing}) - - if err != nil { - panic(fmt.Sprintf("[pemEncode] Error: %v", err)) - } - - return buf.String() -} diff --git a/k8-operator/packages/template/pem_chain.go b/k8-operator/packages/template/pem_chain.go deleted file mode 100644 index 00c4f5d3f..000000000 --- a/k8-operator/packages/template/pem_chain.go +++ /dev/null @@ -1,117 +0,0 @@ -package template - -import ( - "bytes" - "crypto/x509" - "encoding/pem" - "fmt" -) - -const ( - errNilCert = "certificate is nil" - errFoundDisjunctCert = "found multiple leaf or disjunct certificates" - errNoLeafFound = "no leaf certificate found" - errChainCycle = "constructing chain resulted in cycle" -) - -type node struct { - cert *x509.Certificate - parent *node - isParent bool -} - -func fetchX509CertChains(data []byte) []*x509.Certificate { - var newCertChain []*x509.Certificate - nodes := pemToNodes(data) - - // at the end of this computation, the output will be a single linked list - // the tail of the list will be the root node (which has no parents) - // the head of the list will be the leaf node (whose parent will be intermediate certs) - // (head) leaf -> intermediates -> root (tail) - for i := range nodes { - for j := range nodes { - // ignore same node to prevent generating a cycle - if i == j { - continue - } - // if ith node AuthorityKeyId is same as jth node SubjectKeyId, jth node was used - // to sign the ith certificate - if bytes.Equal(nodes[i].cert.AuthorityKeyId, nodes[j].cert.SubjectKeyId) { - nodes[j].isParent = true - nodes[i].parent = nodes[j] - break - } - } - } - - var foundLeaf bool - var leaf *node - for i := range nodes { - if !nodes[i].isParent { - if foundLeaf { - panic(fmt.Sprintf("[fetchX509CertChains] Error: %v", errFoundDisjunctCert)) - } - // this is the leaf node as it's not a parent for any other node - leaf = nodes[i] - foundLeaf = true - } - } - - if leaf == nil { - panic(fmt.Sprintf("[fetchX509CertChains] Error: %v", errNoLeafFound)) - } - - processedNodes := 0 - // iterate through the directed list and append the nodes to new cert chain - for leaf != nil { - processedNodes++ - // ensure we aren't stuck in a cyclic loop - if processedNodes > len(nodes) { - panic(fmt.Sprintf("[fetchX509CertChains] Error: %v", errChainCycle)) - } - newCertChain = append(newCertChain, leaf.cert) - leaf = leaf.parent - } - return newCertChain -} - -func fetchCertChains(data []byte) []byte { - var pemData []byte - newCertChain := fetchX509CertChains(data) - - for _, cert := range newCertChain { - b := &pem.Block{ - Type: pemTypeCertificate, - Bytes: cert.Raw, - } - pemData = append(pemData, pem.EncodeToMemory(b)...) - } - return pemData -} - -func pemToNodes(data []byte) []*node { - nodes := make([]*node, 0) - for { - // decode pem to der first - block, rest := pem.Decode(data) - data = rest - - if block == nil { - break - } - cert, err := x509.ParseCertificate(block.Bytes) - if err != nil { - panic(fmt.Sprintf("[pemToNodes] Error: %v", err)) - } - - if cert == nil { - panic(fmt.Sprintf("[pemToNodes] Error: %v", errNilCert)) - } - nodes = append(nodes, &node{ - cert: cert, - parent: nil, - isParent: false, - }) - } - return nodes -} diff --git a/k8-operator/packages/template/pkcs12.go b/k8-operator/packages/template/pkcs12.go deleted file mode 100644 index e6763fc46..000000000 --- a/k8-operator/packages/template/pkcs12.go +++ /dev/null @@ -1,144 +0,0 @@ -package template - -import ( - "bytes" - "crypto/x509" - "encoding/base64" - "encoding/pem" - "fmt" - - gopkcs12 "software.sslmate.com/src/go-pkcs12" -) - -func pkcs12keyPass(pass, input string) string { - privateKey, _, _, err := gopkcs12.DecodeChain([]byte(input), pass) - if err != nil { - panic(fmt.Sprintf("Error: %v", err)) - } - - marshalPrivateKey, err := x509.MarshalPKCS8PrivateKey(privateKey) - if err != nil { - panic(fmt.Sprintf("Error: %v", err)) - } - - var buf bytes.Buffer - if err := pem.Encode(&buf, &pem.Block{ - Type: pemTypeKey, - Bytes: marshalPrivateKey, - }); err != nil { - panic(fmt.Sprintf("Error: %v", err)) - } - return buf.String() -} - -func parsePrivateKey(block []byte) any { - if k, err := x509.ParsePKCS1PrivateKey(block); err == nil { - return k - } - if k, err := x509.ParsePKCS8PrivateKey(block); err == nil { - return k - } - if k, err := x509.ParseECPrivateKey(block); err == nil { - return k - } - panic("Error: unable to parse private key") -} - -func pkcs12key(input string) string { - return pkcs12keyPass("", input) -} - -func pkcs12certPass(pass, input string) string { - _, certificate, caCerts, err := gopkcs12.DecodeChain([]byte(input), pass) - if err != nil { - panic(fmt.Sprintf("Error: %v", err)) - } - - var pemData []byte - var buf bytes.Buffer - if err := pem.Encode(&buf, &pem.Block{ - Type: pemTypeCertificate, - Bytes: certificate.Raw, - }); err != nil { - panic(fmt.Sprintf("Error: %v", err)) - } - - pemData = append(pemData, buf.Bytes()...) - - for _, ca := range caCerts { - var buf bytes.Buffer - if err := pem.Encode(&buf, &pem.Block{ - Type: pemTypeCertificate, - Bytes: ca.Raw, - }); err != nil { - panic(fmt.Sprintf("Error: %v", err)) - } - pemData = append(pemData, buf.Bytes()...) - } - - // try to order certificate chain. If it fails we return - // the unordered raw pem data. - // This fails if multiple leaf or disjunct certs are provided. - ordered := fetchCertChains(pemData) - - return string(ordered) -} - -func pkcs12cert(input string) string { - return pkcs12certPass("", input) -} - -func pemToPkcs12(cert, key string) string { - return pemToPkcs12Pass(cert, key, "") -} - -func pemToPkcs12Pass(cert, key, pass string) string { - certPem, _ := pem.Decode([]byte(cert)) - - parsedCert, err := x509.ParseCertificate(certPem.Bytes) - if err != nil { - panic(fmt.Sprintf("Error: %v", err)) - } - - return certsToPkcs12(parsedCert, key, nil, pass) -} - -func fullPemToPkcs12(cert, key string) string { - return fullPemToPkcs12Pass(cert, key, "") -} - -func fullPemToPkcs12Pass(cert, key, pass string) string { - certPem, rest := pem.Decode([]byte(cert)) - - parsedCert, err := x509.ParseCertificate(certPem.Bytes) - if err != nil { - panic(fmt.Sprintf("Error: %v", err)) - } - - caCerts := make([]*x509.Certificate, 0) - for len(rest) > 0 { - caPem, restBytes := pem.Decode(rest) - rest = restBytes - - caCert, err := x509.ParseCertificate(caPem.Bytes) - if err != nil { - panic(fmt.Sprintf("Error: %v", err)) - } - - caCerts = append(caCerts, caCert) - } - - return certsToPkcs12(parsedCert, key, caCerts, pass) -} - -func certsToPkcs12(cert *x509.Certificate, key string, caCerts []*x509.Certificate, password string) string { - keyPem, _ := pem.Decode([]byte(key)) - parsedKey := parsePrivateKey(keyPem.Bytes) - - pfx, err := gopkcs12.Modern.Encode(parsedKey, cert, caCerts, password) - if err != nil { - panic(fmt.Sprintf("Error: %v", err)) - } - - return base64.StdEncoding.EncodeToString(pfx) -} diff --git a/k8-operator/packages/template/template.go b/k8-operator/packages/template/template.go deleted file mode 100644 index d56b3da5d..000000000 --- a/k8-operator/packages/template/template.go +++ /dev/null @@ -1,67 +0,0 @@ -package template - -import ( - tpl "text/template" - - "github.com/Masterminds/sprig/v3" -) - -var customInfisicalSecretTemplateFunctions = tpl.FuncMap{ - "pkcs12key": pkcs12key, - "pkcs12keyPass": pkcs12keyPass, - "pkcs12cert": pkcs12cert, - "pkcs12certPass": pkcs12certPass, - - "pemToPkcs12": pemToPkcs12, - "pemToPkcs12Pass": pemToPkcs12Pass, - "fullPemToPkcs12": fullPemToPkcs12, - "fullPemToPkcs12Pass": fullPemToPkcs12Pass, - - "filterPEM": filterPEM, - "filterCertChain": filterCertChain, - - "jwkPublicKeyPem": jwkPublicKeyPem, - "jwkPrivateKeyPem": jwkPrivateKeyPem, - - "toYaml": toYAML, - "fromYaml": fromYAML, - - "decodeBase64ToBytes": decodeBase64ToBytes, - "encodeBase64": encodeBase64, -} - -const ( - errParse = "unable to parse template at key %s: %s" - errExecute = "unable to execute template at key %s: %s" - errDecodePKCS12WithPass = "unable to decode pkcs12 with password: %s" - errDecodeCertWithPass = "unable to decode pkcs12 certificate with password: %s" - errParsePrivKey = "unable to parse private key type" - errUnmarshalJSON = "unable to unmarshal json: %s" - errMarshalJSON = "unable to marshal json: %s" - - pemTypeCertificate = "CERTIFICATE" - pemTypeKey = "PRIVATE KEY" -) - -func InitializeTemplateFunctions() { - templates := customInfisicalSecretTemplateFunctions - - sprigFuncs := sprig.TxtFuncMap() - // removed for security reasons - delete(sprigFuncs, "env") - delete(sprigFuncs, "expandenv") - - for k, v := range sprigFuncs { - // make sure we aren't overwriting any of our own functions - _, exists := templates[k] - if !exists { - templates[k] = v - } - } - - customInfisicalSecretTemplateFunctions = templates -} - -func GetTemplateFunctions() tpl.FuncMap { - return customInfisicalSecretTemplateFunctions -} diff --git a/k8-operator/packages/template/yaml.go b/k8-operator/packages/template/yaml.go deleted file mode 100644 index 5352d5a02..000000000 --- a/k8-operator/packages/template/yaml.go +++ /dev/null @@ -1,30 +0,0 @@ -package template - -import ( - "fmt" - "strings" - - "gopkg.in/yaml.v3" -) - -func toYAML(v any) string { - data, err := yaml.Marshal(v) - if err != nil { - panic(fmt.Sprintf("Error: %v", err)) - - } - return strings.TrimSuffix(string(data), "\n") -} - -// fromYAML converts a YAML document into a map[string]any. -// -// This is not a general-purpose YAML parser, and will not parse all valid -// YAML documents. -func fromYAML(str string) map[string]any { - mapData := map[string]any{} - - if err := yaml.Unmarshal([]byte(str), &mapData); err != nil { - panic(fmt.Sprintf("Error: %v", err)) - } - return mapData -} diff --git a/k8-operator/packages/util/helpers.go b/k8-operator/packages/util/helpers.go deleted file mode 100644 index ef3712715..000000000 --- a/k8-operator/packages/util/helpers.go +++ /dev/null @@ -1,56 +0,0 @@ -package util - -import ( - "fmt" - "strconv" - "strings" - "time" -) - -func ConvertIntervalToDuration(resyncInterval *string) (time.Duration, error) { - - if resyncInterval == nil || *resyncInterval == "" { - return 0, nil - } - - length := len(*resyncInterval) - if length < 2 { - return 0, fmt.Errorf("invalid format") - } - - unit := (*resyncInterval)[length-1:] - numberPart := (*resyncInterval)[:length-1] - - number, err := strconv.Atoi(numberPart) - if err != nil { - return 0, err - } - - switch unit { - case "s": - if number < 5 { - return 0, fmt.Errorf("resync interval must be at least 5 seconds") - } - return time.Duration(number) * time.Second, nil - case "m": - return time.Duration(number) * time.Minute, nil - case "h": - return time.Duration(number) * time.Hour, nil - case "d": - return time.Duration(number) * 24 * time.Hour, nil - case "w": - return time.Duration(number) * 7 * 24 * time.Hour, nil - default: - return 0, fmt.Errorf("invalid time unit") - } -} - -func AppendAPIEndpoint(address string) string { - if strings.HasSuffix(address, "/api") { - return address - } - if address[len(address)-1] == '/' { - return address + "api" - } - return address + "/api" -} diff --git a/k8-operator/packages/util/models.go b/k8-operator/packages/util/models.go deleted file mode 100644 index 8030731c2..000000000 --- a/k8-operator/packages/util/models.go +++ /dev/null @@ -1,13 +0,0 @@ -package util - -import ( - "context" - - infisicalSdk "github.com/infisical/go-sdk" -) - -type ResourceVariables struct { - InfisicalClient infisicalSdk.InfisicalClientInterface - CancelCtx context.CancelFunc - AuthDetails AuthenticationDetails -} diff --git a/k8-operator/packages/util/secrets.go b/k8-operator/packages/util/secrets.go deleted file mode 100644 index 22fa75271..000000000 --- a/k8-operator/packages/util/secrets.go +++ /dev/null @@ -1,186 +0,0 @@ -package util - -import ( - "fmt" - "strings" - - "github.com/Infisical/infisical/k8-operator/api/v1alpha1" - "github.com/Infisical/infisical/k8-operator/packages/api" - "github.com/Infisical/infisical/k8-operator/packages/model" - "github.com/go-resty/resty/v2" - infisical "github.com/infisical/go-sdk" -) - -type DecodedSymmetricEncryptionDetails = struct { - Cipher []byte - IV []byte - Tag []byte - Key []byte -} - -func VerifyServiceToken(serviceToken string) (string, error) { - serviceTokenParts := strings.SplitN(serviceToken, ".", 4) - if len(serviceTokenParts) < 4 { - return "", fmt.Errorf("invalid service token entered. Please double check your service token and try again") - } - - serviceToken = fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2]) - return serviceToken, nil -} - -func GetServiceTokenDetails(infisicalToken string) (api.GetServiceTokenDetailsResponse, error) { - serviceTokenParts := strings.SplitN(infisicalToken, ".", 4) - if len(serviceTokenParts) < 4 { - return api.GetServiceTokenDetailsResponse{}, fmt.Errorf("invalid service token entered. Please double check your service token and try again") - } - - serviceToken := fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2]) - - httpClient := resty.New() - httpClient.SetAuthToken(serviceToken). - SetHeader("Accept", "application/json") - - serviceTokenDetails, err := api.CallGetServiceTokenDetailsV2(httpClient) - if err != nil { - return api.GetServiceTokenDetailsResponse{}, fmt.Errorf("unable to get service token details. [err=%v]", err) - } - - return serviceTokenDetails, nil -} - -func GetPlainTextSecretsViaMachineIdentity(infisicalClient infisical.InfisicalClientInterface, secretScope v1alpha1.MachineIdentityScopeInWorkspace) ([]model.SingleEnvironmentVariable, error) { - - secrets, err := infisicalClient.Secrets().List(infisical.ListSecretsOptions{ - ProjectSlug: secretScope.ProjectSlug, - Environment: secretScope.EnvSlug, - Recursive: secretScope.Recursive, - SecretPath: secretScope.SecretsPath, - IncludeImports: true, - ExpandSecretReferences: true, - }) - - if err != nil { - return nil, fmt.Errorf("unable to get secrets. [err=%v]", err) - } - - var environmentVariables []model.SingleEnvironmentVariable - - for _, secret := range secrets { - - environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{ - Key: secret.SecretKey, - Value: secret.SecretValue, - Type: secret.Type, - ID: secret.ID, - SecretPath: secret.SecretPath, - }) - } - - return environmentVariables, nil -} - -func GetPlainTextSecretsViaServiceToken(infisicalClient infisical.InfisicalClientInterface, fullServiceToken string, envSlug string, secretPath string, recursive bool) ([]model.SingleEnvironmentVariable, error) { - serviceTokenParts := strings.SplitN(fullServiceToken, ".", 4) - if len(serviceTokenParts) < 4 { - return nil, fmt.Errorf("invalid service token entered. Please double check your service token and try again") - } - - serviceToken := fmt.Sprintf("%v.%v.%v", serviceTokenParts[0], serviceTokenParts[1], serviceTokenParts[2]) - - httpClient := resty.New() - - httpClient.SetAuthToken(serviceToken). - SetHeader("Accept", "application/json") - - serviceTokenDetails, err := api.CallGetServiceTokenDetailsV2(httpClient) - if err != nil { - return nil, fmt.Errorf("unable to get service token details. [err=%v]", err) - } - - secrets, err := infisicalClient.Secrets().List(infisical.ListSecretsOptions{ - ProjectID: serviceTokenDetails.Workspace, - Environment: envSlug, - Recursive: recursive, - SecretPath: secretPath, - IncludeImports: true, - ExpandSecretReferences: true, - }) - - if err != nil { - return nil, err - } - - var environmentVariables []model.SingleEnvironmentVariable - - for _, secret := range secrets { - - environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{ - Key: secret.SecretKey, - Value: secret.SecretValue, - Type: secret.Type, - ID: secret.ID, - SecretPath: secret.SecretPath, - }) - } - - return environmentVariables, nil - -} - -// Fetches plaintext secrets from an API endpoint using a service account. -// The function fetches the service account details and keys, decrypts the workspace key, fetches the encrypted secrets for the specified project and environment, and decrypts the secrets using the decrypted workspace key. -// Returns the plaintext secrets, encrypted secrets response, and any errors that occurred during the process. -func GetPlainTextSecretsViaServiceAccount(infisicalClient infisical.InfisicalClientInterface, serviceAccountCreds model.ServiceAccountDetails, projectId string, environmentName string) ([]model.SingleEnvironmentVariable, error) { - httpClient := resty.New() - httpClient.SetAuthToken(serviceAccountCreds.AccessKey). - SetHeader("Accept", "application/json") - - serviceAccountDetails, err := api.CallGetServiceTokenAccountDetailsV2(httpClient) - if err != nil { - return nil, fmt.Errorf("GetPlainTextSecretsViaServiceAccount: unable to get service account details. [err=%v]", err) - } - - serviceAccountKeys, err := api.CallGetServiceAccountKeysV2(httpClient, api.GetServiceAccountKeysRequest{ServiceAccountId: serviceAccountDetails.ServiceAccount.ID}) - if err != nil { - return nil, fmt.Errorf("GetPlainTextSecretsViaServiceAccount: unable to get service account key details. [err=%v]", err) - } - - // find key for requested project - var workspaceServiceAccountKey api.ServiceAccountKey - for _, serviceAccountKey := range serviceAccountKeys.ServiceAccountKeys { - if serviceAccountKey.Workspace == projectId { - workspaceServiceAccountKey = serviceAccountKey - } - } - - if workspaceServiceAccountKey.ID == "" || workspaceServiceAccountKey.EncryptedKey == "" || workspaceServiceAccountKey.Nonce == "" || serviceAccountCreds.PublicKey == "" || serviceAccountCreds.PrivateKey == "" { - return nil, fmt.Errorf("unable to find key for [projectId=%s] [err=%v]. Ensure that the given service account has access to given projectId", projectId, err) - } - - secrets, err := infisicalClient.Secrets().List(infisical.ListSecretsOptions{ - ProjectID: projectId, - Environment: environmentName, - Recursive: false, - SecretPath: "/", - IncludeImports: true, - ExpandSecretReferences: true, - }) - - if err != nil { - return nil, err - } - - var environmentVariables []model.SingleEnvironmentVariable - - for _, secret := range secrets { - environmentVariables = append(environmentVariables, model.SingleEnvironmentVariable{ - Key: secret.SecretKey, - Value: secret.SecretValue, - Type: secret.Type, - ID: secret.ID, - SecretPath: secret.SecretPath, - }) - } - - return environmentVariables, nil -} diff --git a/k8-operator/packages/util/time.go b/k8-operator/packages/util/time.go deleted file mode 100644 index 0b78a16a6..000000000 --- a/k8-operator/packages/util/time.go +++ /dev/null @@ -1,40 +0,0 @@ -package util - -import ( - "fmt" - "strconv" - "time" -) - -func ConvertResyncIntervalToDuration(resyncInterval string) (time.Duration, error) { - length := len(resyncInterval) - if length < 2 { - return 0, fmt.Errorf("invalid format") - } - - unit := resyncInterval[length-1:] - numberPart := resyncInterval[:length-1] - - number, err := strconv.Atoi(numberPart) - if err != nil { - return 0, err - } - - switch unit { - case "s": - if number < 5 { - return 0, fmt.Errorf("resync interval must be at least 5 seconds") - } - return time.Duration(number) * time.Second, nil - case "m": - return time.Duration(number) * time.Minute, nil - case "h": - return time.Duration(number) * time.Hour, nil - case "d": - return time.Duration(number) * 24 * time.Hour, nil - case "w": - return time.Duration(number) * 7 * 24 * time.Hour, nil - default: - return 0, fmt.Errorf("invalid time unit") - } -} diff --git a/k8-operator/packages/util/workspace.go b/k8-operator/packages/util/workspace.go deleted file mode 100644 index ad3694fcf..000000000 --- a/k8-operator/packages/util/workspace.go +++ /dev/null @@ -1,27 +0,0 @@ -package util - -import ( - "fmt" - - "github.com/Infisical/infisical/k8-operator/packages/api" - "github.com/Infisical/infisical/k8-operator/packages/model" - "github.com/go-resty/resty/v2" -) - -func GetProjectByID(accessToken string, projectId string) (model.Project, error) { - - httpClient := resty.New() - httpClient. - SetAuthScheme("Bearer"). - SetAuthToken(accessToken). - SetHeader("Accept", "application/json") - - projectDetails, err := api.CallGetProjectByID(httpClient, api.GetProjectByIDRequest{ - ProjectID: projectId, - }) - if err != nil { - return model.Project{}, fmt.Errorf("unable to get project by slug. [err=%v]", err) - } - - return projectDetails.Project, nil -} diff --git a/k8-operator/k8-operator/test/e2e/e2e_suite_test.go b/k8-operator/test/e2e/e2e_suite_test.go similarity index 100% rename from k8-operator/k8-operator/test/e2e/e2e_suite_test.go rename to k8-operator/test/e2e/e2e_suite_test.go diff --git a/k8-operator/k8-operator/test/e2e/e2e_test.go b/k8-operator/test/e2e/e2e_test.go similarity index 100% rename from k8-operator/k8-operator/test/e2e/e2e_test.go rename to k8-operator/test/e2e/e2e_test.go diff --git a/k8-operator/k8-operator/test/utils/utils.go b/k8-operator/test/utils/utils.go similarity index 100% rename from k8-operator/k8-operator/test/utils/utils.go rename to k8-operator/test/utils/utils.go From 035ac0fe8d3d1da4c11e749234e163792cecf92a Mon Sep 17 00:00:00 2001 From: = Date: Wed, 6 Aug 2025 16:37:55 +0530 Subject: [PATCH 3/9] feat: resolved merge conflict --- .../infisicalsecret/infisicalsecret_helper.go | 570 ------------------ .../services/infisicalsecret/reconciler.go | 1 + k8-operator/internal/util/kubernetes.go | 33 +- k8-operator/packages/util/kubernetes.go | 117 ---- 4 files changed, 30 insertions(+), 691 deletions(-) delete mode 100644 k8-operator/controllers/infisicalsecret/infisicalsecret_helper.go delete mode 100644 k8-operator/packages/util/kubernetes.go diff --git a/k8-operator/controllers/infisicalsecret/infisicalsecret_helper.go b/k8-operator/controllers/infisicalsecret/infisicalsecret_helper.go deleted file mode 100644 index c443738e1..000000000 --- a/k8-operator/controllers/infisicalsecret/infisicalsecret_helper.go +++ /dev/null @@ -1,570 +0,0 @@ -package controllers - -import ( - "bytes" - "context" - "errors" - "fmt" - "strings" - tpl "text/template" - - "github.com/Infisical/infisical/k8-operator/api/v1alpha1" - "github.com/Infisical/infisical/k8-operator/packages/api" - "github.com/Infisical/infisical/k8-operator/packages/constants" - "github.com/Infisical/infisical/k8-operator/packages/crypto" - "github.com/Infisical/infisical/k8-operator/packages/model" - "github.com/Infisical/infisical/k8-operator/packages/template" - "github.com/Infisical/infisical/k8-operator/packages/util" - "github.com/go-logr/logr" - - "k8s.io/apimachinery/pkg/types" - - infisicalSdk "github.com/infisical/go-sdk" - corev1 "k8s.io/api/core/v1" - k8Errors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func (r *InfisicalSecretReconciler) handleAuthentication(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error) { - - // ? Legacy support, service token auth - infisicalToken, err := r.getInfisicalTokenFromKubeSecret(ctx, infisicalSecret) - if err != nil { - return util.AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get service token from kube secret [err=%s]", err) - } - if infisicalToken != "" { - infisicalClient.Auth().SetAccessToken(infisicalToken) - return util.AuthenticationDetails{AuthStrategy: util.AuthStrategy.SERVICE_TOKEN}, nil - } - - // ? Legacy support, service account auth - serviceAccountCreds, err := r.getInfisicalServiceAccountCredentialsFromKubeSecret(ctx, infisicalSecret) - if err != nil { - return util.AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get service account creds from kube secret [err=%s]", err) - } - - if serviceAccountCreds.AccessKey != "" || serviceAccountCreds.PrivateKey != "" || serviceAccountCreds.PublicKey != "" { - infisicalClient.Auth().SetAccessToken(serviceAccountCreds.AccessKey) - return util.AuthenticationDetails{AuthStrategy: util.AuthStrategy.SERVICE_ACCOUNT}, nil - } - - authStrategies := map[util.AuthStrategyType]func(ctx context.Context, reconcilerClient client.Client, secretCrd util.SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error){ - util.AuthStrategy.UNIVERSAL_MACHINE_IDENTITY: util.HandleUniversalAuth, - util.AuthStrategy.KUBERNETES_MACHINE_IDENTITY: util.HandleKubernetesAuth, - util.AuthStrategy.AWS_IAM_MACHINE_IDENTITY: util.HandleAwsIamAuth, - util.AuthStrategy.AZURE_MACHINE_IDENTITY: util.HandleAzureAuth, - util.AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: util.HandleGcpIdTokenAuth, - util.AuthStrategy.GCP_IAM_MACHINE_IDENTITY: util.HandleGcpIamAuth, - util.AuthStrategy.LDAP_MACHINE_IDENTITY: util.HandleLdapAuth, - } - - for authStrategy, authHandler := range authStrategies { - authDetails, err := authHandler(ctx, r.Client, util.SecretAuthInput{ - Secret: infisicalSecret, - Type: util.SecretCrd.INFISICAL_SECRET, - }, infisicalClient) - - if err == nil { - return authDetails, nil - } - - if !errors.Is(err, util.ErrAuthNotApplicable) { - return util.AuthenticationDetails{}, fmt.Errorf("authentication failed for strategy [%s] [err=%w]", authStrategy, err) - } - } - - return util.AuthenticationDetails{}, fmt.Errorf("no authentication method provided") - -} - -func (r *InfisicalSecretReconciler) getInfisicalTokenFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (string, error) { - // default to new secret ref structure - secretName := infisicalSecret.Spec.Authentication.ServiceToken.ServiceTokenSecretReference.SecretName - secretNamespace := infisicalSecret.Spec.Authentication.ServiceToken.ServiceTokenSecretReference.SecretNamespace - // fall back to previous secret ref - if secretName == "" { - secretName = infisicalSecret.Spec.TokenSecretReference.SecretName - } - - if secretNamespace == "" { - secretNamespace = infisicalSecret.Spec.TokenSecretReference.SecretNamespace - } - - tokenSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{ - Namespace: secretNamespace, - Name: secretName, - }) - - if k8Errors.IsNotFound(err) { - return "", nil - } - - if err != nil { - return "", fmt.Errorf("failed to read Infisical token secret from secret named [%s] in namespace [%s]: with error [%w]", infisicalSecret.Spec.TokenSecretReference.SecretName, infisicalSecret.Spec.TokenSecretReference.SecretNamespace, err) - } - - infisicalServiceToken := tokenSecret.Data[constants.INFISICAL_TOKEN_SECRET_KEY_NAME] - - return strings.Replace(string(infisicalServiceToken), " ", "", -1), nil -} - -func (r *InfisicalSecretReconciler) getInfisicalCaCertificateFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (caCertificate string, err error) { - - caCertificateFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{ - Namespace: infisicalSecret.Spec.TLS.CaRef.SecretNamespace, - Name: infisicalSecret.Spec.TLS.CaRef.SecretName, - }) - - if k8Errors.IsNotFound(err) { - return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err) - } - - if err != nil { - return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err) - } - - caCertificateFromSecret := string(caCertificateFromKubeSecret.Data[infisicalSecret.Spec.TLS.CaRef.SecretKey]) - - return caCertificateFromSecret, nil -} - -// Fetches service account credentials from a Kubernetes secret specified in the infisicalSecret object, extracts the access key, public key, and private key from the secret, and returns them as a ServiceAccountCredentials object. -// If any keys are missing or an error occurs, returns an empty object or an error object, respectively. -func (r *InfisicalSecretReconciler) getInfisicalServiceAccountCredentialsFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (serviceAccountDetails model.ServiceAccountDetails, err error) { - serviceAccountCredsFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{ - Namespace: infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretNamespace, - Name: infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretName, - }) - - if k8Errors.IsNotFound(err) { - return model.ServiceAccountDetails{}, nil - } - - if err != nil { - return model.ServiceAccountDetails{}, fmt.Errorf("something went wrong when fetching your service account credentials [err=%s]", err) - } - - accessKeyFromSecret := serviceAccountCredsFromKubeSecret.Data[constants.SERVICE_ACCOUNT_ACCESS_KEY] - publicKeyFromSecret := serviceAccountCredsFromKubeSecret.Data[constants.SERVICE_ACCOUNT_PUBLIC_KEY] - privateKeyFromSecret := serviceAccountCredsFromKubeSecret.Data[constants.SERVICE_ACCOUNT_PRIVATE_KEY] - - if accessKeyFromSecret == nil || publicKeyFromSecret == nil || privateKeyFromSecret == nil { - return model.ServiceAccountDetails{}, nil - } - - return model.ServiceAccountDetails{AccessKey: string(accessKeyFromSecret), PrivateKey: string(privateKeyFromSecret), PublicKey: string(publicKeyFromSecret)}, nil -} - -func convertBinaryToStringMap(binaryMap map[string][]byte) map[string]string { - stringMap := make(map[string]string) - for k, v := range binaryMap { - stringMap[k] = string(v) - } - return stringMap -} - -func (r *InfisicalSecretReconciler) createInfisicalManagedKubeResource(ctx context.Context, logger logr.Logger, infisicalSecret v1alpha1.InfisicalSecret, managedSecretReferenceInterface interface{}, secretsFromAPI []model.SingleEnvironmentVariable, ETag string, resourceType constants.ManagedKubeResourceType) error { - plainProcessedSecrets := make(map[string][]byte) - - var managedTemplateData *v1alpha1.SecretTemplate - - if resourceType == constants.MANAGED_KUBE_RESOURCE_TYPE_SECRET { - managedTemplateData = managedSecretReferenceInterface.(v1alpha1.ManagedKubeSecretConfig).Template - } else if resourceType == constants.MANAGED_KUBE_RESOURCE_TYPE_CONFIG_MAP { - managedTemplateData = managedSecretReferenceInterface.(v1alpha1.ManagedKubeConfigMapConfig).Template - } - - if managedTemplateData == nil || managedTemplateData.IncludeAllSecrets { - for _, secret := range secretsFromAPI { - plainProcessedSecrets[secret.Key] = []byte(secret.Value) // plain process - } - } - - if managedTemplateData != nil { - secretKeyValue := make(map[string]model.SecretTemplateOptions) - for _, secret := range secretsFromAPI { - secretKeyValue[secret.Key] = model.SecretTemplateOptions{ - Value: secret.Value, - SecretPath: secret.SecretPath, - } - } - - for templateKey, userTemplate := range managedTemplateData.Data { - tmpl, err := tpl.New("secret-templates").Funcs(template.GetTemplateFunctions()).Parse(userTemplate) - if err != nil { - return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err) - } - - buf := bytes.NewBuffer(nil) - err = tmpl.Execute(buf, secretKeyValue) - if err != nil { - return fmt.Errorf("unable to execute template: %s [err=%v]", templateKey, err) - } - plainProcessedSecrets[templateKey] = buf.Bytes() - } - } - - // copy labels and annotations from InfisicalSecret CRD - labels := map[string]string{} - for k, v := range infisicalSecret.Labels { - labels[k] = v - } - - annotations := map[string]string{} - systemPrefixes := []string{"kubectl.kubernetes.io/", "kubernetes.io/", "k8s.io/", "helm.sh/"} - for k, v := range infisicalSecret.Annotations { - isSystem := false - for _, prefix := range systemPrefixes { - if strings.HasPrefix(k, prefix) { - isSystem = true - break - } - } - if !isSystem { - annotations[k] = v - } - } - - if resourceType == constants.MANAGED_KUBE_RESOURCE_TYPE_SECRET { - - managedSecretReference := managedSecretReferenceInterface.(v1alpha1.ManagedKubeSecretConfig) - - annotations[constants.SECRET_VERSION_ANNOTATION] = ETag - // create a new secret as specified by the managed secret spec of CRD - newKubeSecretInstance := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: managedSecretReference.SecretName, - Namespace: managedSecretReference.SecretNamespace, - Annotations: annotations, - Labels: labels, - }, - Type: corev1.SecretType(managedSecretReference.SecretType), - Data: plainProcessedSecrets, - } - - if managedSecretReference.CreationPolicy == "Owner" { - // Set InfisicalSecret instance as the owner and controller of the managed secret - err := ctrl.SetControllerReference(&infisicalSecret, newKubeSecretInstance, r.Scheme) - if err != nil { - return err - } - } - - err := r.Client.Create(ctx, newKubeSecretInstance) - if err != nil { - return fmt.Errorf("unable to create the managed Kubernetes secret : %w", err) - } - logger.Info(fmt.Sprintf("Successfully created a managed Kubernetes secret with your Infisical secrets. Type: %s", managedSecretReference.SecretType)) - return nil - } else if resourceType == constants.MANAGED_KUBE_RESOURCE_TYPE_CONFIG_MAP { - - managedSecretReference := managedSecretReferenceInterface.(v1alpha1.ManagedKubeConfigMapConfig) - - // create a new config map as specified by the managed secret spec of CRD - newKubeConfigMapInstance := &corev1.ConfigMap{ - ObjectMeta: metav1.ObjectMeta{ - Name: managedSecretReference.ConfigMapName, - Namespace: managedSecretReference.ConfigMapNamespace, - Annotations: annotations, - Labels: labels, - }, - Data: convertBinaryToStringMap(plainProcessedSecrets), - } - - if managedSecretReference.CreationPolicy == "Owner" { - // Set InfisicalSecret instance as the owner and controller of the managed config map - err := ctrl.SetControllerReference(&infisicalSecret, newKubeConfigMapInstance, r.Scheme) - if err != nil { - return err - } - } - - err := r.Client.Create(ctx, newKubeConfigMapInstance) - if err != nil { - return fmt.Errorf("unable to create the managed Kubernetes config map : %w", err) - } - logger.Info(fmt.Sprintf("Successfully created a managed Kubernetes config map with your Infisical secrets. Type: %s", managedSecretReference.ConfigMapName)) - return nil - - } - return fmt.Errorf("invalid resource type") - -} - -func (r *InfisicalSecretReconciler) updateInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, managedSecretReference v1alpha1.ManagedKubeSecretConfig, managedKubeSecret corev1.Secret, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error { - managedTemplateData := managedSecretReference.Template - - plainProcessedSecrets := make(map[string][]byte) - if managedTemplateData == nil || managedTemplateData.IncludeAllSecrets { - for _, secret := range secretsFromAPI { - plainProcessedSecrets[secret.Key] = []byte(secret.Value) - } - } - - if managedTemplateData != nil { - secretKeyValue := make(map[string]model.SecretTemplateOptions) - for _, secret := range secretsFromAPI { - secretKeyValue[secret.Key] = model.SecretTemplateOptions{ - Value: secret.Value, - SecretPath: secret.SecretPath, - } - } - - for templateKey, userTemplate := range managedTemplateData.Data { - tmpl, err := tpl.New("secret-templates").Funcs(template.GetTemplateFunctions()).Parse(userTemplate) - if err != nil { - return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err) - } - - buf := bytes.NewBuffer(nil) - err = tmpl.Execute(buf, secretKeyValue) - if err != nil { - return fmt.Errorf("unable to execute template: %s [err=%v]", templateKey, err) - } - plainProcessedSecrets[templateKey] = buf.Bytes() - } - } - - // Initialize the Annotations map if it's nil - if managedKubeSecret.ObjectMeta.Annotations == nil { - managedKubeSecret.ObjectMeta.Annotations = make(map[string]string) - } - - managedKubeSecret.Data = plainProcessedSecrets - managedKubeSecret.ObjectMeta.Annotations[constants.SECRET_VERSION_ANNOTATION] = ETag - - err := r.Client.Update(ctx, &managedKubeSecret) - if err != nil { - return fmt.Errorf("unable to update Kubernetes secret because [%w]", err) - } - - logger.Info("successfully updated managed Kubernetes secret") - return nil -} - -func (r *InfisicalSecretReconciler) updateInfisicalManagedConfigMap(ctx context.Context, logger logr.Logger, managedConfigMapReference v1alpha1.ManagedKubeConfigMapConfig, managedConfigMap corev1.ConfigMap, secretsFromAPI []model.SingleEnvironmentVariable, ETag string) error { - managedTemplateData := managedConfigMapReference.Template - - plainProcessedSecrets := make(map[string][]byte) - if managedTemplateData == nil || managedTemplateData.IncludeAllSecrets { - for _, secret := range secretsFromAPI { - plainProcessedSecrets[secret.Key] = []byte(secret.Value) - } - } - - if managedTemplateData != nil { - secretKeyValue := make(map[string]model.SecretTemplateOptions) - for _, secret := range secretsFromAPI { - secretKeyValue[secret.Key] = model.SecretTemplateOptions{ - Value: secret.Value, - SecretPath: secret.SecretPath, - } - } - - for templateKey, userTemplate := range managedTemplateData.Data { - tmpl, err := tpl.New("secret-templates").Funcs(template.GetTemplateFunctions()).Parse(userTemplate) - if err != nil { - return fmt.Errorf("unable to compile template: %s [err=%v]", templateKey, err) - } - - buf := bytes.NewBuffer(nil) - err = tmpl.Execute(buf, secretKeyValue) - if err != nil { - return fmt.Errorf("unable to execute template: %s [err=%v]", templateKey, err) - } - plainProcessedSecrets[templateKey] = buf.Bytes() - } - } - - // Initialize the Annotations map if it's nil - if managedConfigMap.ObjectMeta.Annotations == nil { - managedConfigMap.ObjectMeta.Annotations = make(map[string]string) - } - - managedConfigMap.Data = convertBinaryToStringMap(plainProcessedSecrets) - managedConfigMap.ObjectMeta.Annotations[constants.SECRET_VERSION_ANNOTATION] = ETag - - err := r.Client.Update(ctx, &managedConfigMap) - if err != nil { - return fmt.Errorf("unable to update Kubernetes config map because [%w]", err) - } - - logger.Info("successfully updated managed Kubernetes config map") - return nil -} - -func (r *InfisicalSecretReconciler) fetchSecretsFromAPI(ctx context.Context, logger logr.Logger, authDetails util.AuthenticationDetails, infisicalClient infisicalSdk.InfisicalClientInterface, infisicalSecret v1alpha1.InfisicalSecret) ([]model.SingleEnvironmentVariable, error) { - - if authDetails.AuthStrategy == util.AuthStrategy.SERVICE_ACCOUNT { // Service Account // ! Legacy auth method - serviceAccountCreds, err := r.getInfisicalServiceAccountCredentialsFromKubeSecret(ctx, infisicalSecret) - if err != nil { - return nil, fmt.Errorf("ReconcileInfisicalSecret: unable to get service account creds from kube secret [err=%s]", err) - } - - plainTextSecretsFromApi, err := util.GetPlainTextSecretsViaServiceAccount(infisicalClient, serviceAccountCreds, infisicalSecret.Spec.Authentication.ServiceAccount.ProjectId, infisicalSecret.Spec.Authentication.ServiceAccount.EnvironmentName) - if err != nil { - return nil, fmt.Errorf("\nfailed to get secrets because [err=%v]", err) - } - - logger.Info("ReconcileInfisicalSecret: Fetched secrets via service account") - - return plainTextSecretsFromApi, nil - - } else if authDetails.AuthStrategy == util.AuthStrategy.SERVICE_TOKEN { // Service Tokens // ! Legacy / Deprecated auth method - infisicalToken, err := r.getInfisicalTokenFromKubeSecret(ctx, infisicalSecret) - if err != nil { - return nil, fmt.Errorf("ReconcileInfisicalSecret: unable to get service token from kube secret [err=%s]", err) - } - - envSlug := infisicalSecret.Spec.Authentication.ServiceToken.SecretsScope.EnvSlug - secretsPath := infisicalSecret.Spec.Authentication.ServiceToken.SecretsScope.SecretsPath - recursive := infisicalSecret.Spec.Authentication.ServiceToken.SecretsScope.Recursive - - plainTextSecretsFromApi, err := util.GetPlainTextSecretsViaServiceToken(infisicalClient, infisicalToken, envSlug, secretsPath, recursive) - if err != nil { - return nil, fmt.Errorf("\nfailed to get secrets because [err=%v]", err) - } - - logger.Info("ReconcileInfisicalSecret: Fetched secrets via [type=SERVICE_TOKEN]") - - return plainTextSecretsFromApi, nil - - } else if authDetails.IsMachineIdentityAuth { // * Machine Identity authentication, the SDK will be authenticated at this point - plainTextSecretsFromApi, err := util.GetPlainTextSecretsViaMachineIdentity(infisicalClient, authDetails.MachineIdentityScope) - - if err != nil { - return nil, fmt.Errorf("\nfailed to get secrets because [err=%v]", err) - } - - logger.Info(fmt.Sprintf("ReconcileInfisicalSecret: Fetched secrets via machine identity [type=%v]", authDetails.AuthStrategy)) - - return plainTextSecretsFromApi, nil - - } else { - return nil, errors.New("no authentication method provided. Please configure a authentication method then try again") - } -} - -func (r *InfisicalSecretReconciler) getResourceVariables(infisicalSecret v1alpha1.InfisicalSecret) util.ResourceVariables { - - var resourceVariables util.ResourceVariables - - if _, ok := infisicalSecretResourceVariablesMap[string(infisicalSecret.UID)]; !ok { - - ctx, cancel := context.WithCancel(context.Background()) - - client := infisicalSdk.NewInfisicalClient(ctx, infisicalSdk.Config{ - SiteUrl: api.API_HOST_URL, - CaCertificate: api.API_CA_CERTIFICATE, - UserAgent: api.USER_AGENT_NAME, - }) - - infisicalSecretResourceVariablesMap[string(infisicalSecret.UID)] = util.ResourceVariables{ - InfisicalClient: client, - CancelCtx: cancel, - AuthDetails: util.AuthenticationDetails{}, - } - - resourceVariables = infisicalSecretResourceVariablesMap[string(infisicalSecret.UID)] - - } else { - resourceVariables = infisicalSecretResourceVariablesMap[string(infisicalSecret.UID)] - } - - return resourceVariables - -} - -func (r *InfisicalSecretReconciler) updateResourceVariables(infisicalSecret v1alpha1.InfisicalSecret, resourceVariables util.ResourceVariables) { - infisicalSecretResourceVariablesMap[string(infisicalSecret.UID)] = resourceVariables -} - -func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, managedKubeSecretReferences []v1alpha1.ManagedKubeSecretConfig, managedKubeConfigMapReferences []v1alpha1.ManagedKubeConfigMapConfig) (int, error) { - - if infisicalSecret == nil { - return 0, fmt.Errorf("infisicalSecret is nil") - } - - resourceVariables := r.getResourceVariables(*infisicalSecret) - infisicalClient := resourceVariables.InfisicalClient - cancelCtx := resourceVariables.CancelCtx - authDetails := resourceVariables.AuthDetails - var err error - - if authDetails.AuthStrategy == "" { - logger.Info("No authentication strategy found. Attempting to authenticate") - authDetails, err = r.handleAuthentication(ctx, *infisicalSecret, infisicalClient) - r.SetInfisicalTokenLoadCondition(ctx, logger, infisicalSecret, authDetails.AuthStrategy, err) - - if err != nil { - return 0, fmt.Errorf("unable to authenticate [err=%s]", err) - } - - r.updateResourceVariables(*infisicalSecret, util.ResourceVariables{ - InfisicalClient: infisicalClient, - CancelCtx: cancelCtx, - AuthDetails: authDetails, - }) - } - - plainTextSecretsFromApi, err := r.fetchSecretsFromAPI(ctx, logger, authDetails, infisicalClient, *infisicalSecret) - - if err != nil { - return 0, fmt.Errorf("failed to fetch secrets from API for managed secrets [err=%s]", err) - } - secretsCount := len(plainTextSecretsFromApi) - - if len(managedKubeSecretReferences) > 0 { - for _, managedSecretReference := range managedKubeSecretReferences { - // Look for managed secret by name and namespace - managedKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{ - Name: managedSecretReference.SecretName, - Namespace: managedSecretReference.SecretNamespace, - }) - - if err != nil && !k8Errors.IsNotFound(err) { - return 0, fmt.Errorf("something went wrong when fetching the managed Kubernetes secret [%w]", err) - } - - newEtag := crypto.ComputeEtag([]byte(fmt.Sprintf("%v", plainTextSecretsFromApi))) - if managedKubeSecret == nil { - if err := r.createInfisicalManagedKubeResource(ctx, logger, *infisicalSecret, managedSecretReference, plainTextSecretsFromApi, newEtag, constants.MANAGED_KUBE_RESOURCE_TYPE_SECRET); err != nil { - return 0, fmt.Errorf("failed to create managed secret [err=%s]", err) - } - } else { - if err := r.updateInfisicalManagedKubeSecret(ctx, logger, managedSecretReference, *managedKubeSecret, plainTextSecretsFromApi, newEtag); err != nil { - return 0, fmt.Errorf("failed to update managed secret [err=%s]", err) - } - } - } - } - - if len(managedKubeConfigMapReferences) > 0 { - for _, managedConfigMapReference := range managedKubeConfigMapReferences { - managedKubeConfigMap, err := util.GetKubeConfigMapByNamespacedName(ctx, r.Client, types.NamespacedName{ - Name: managedConfigMapReference.ConfigMapName, - Namespace: managedConfigMapReference.ConfigMapNamespace, - }) - - if err != nil && !k8Errors.IsNotFound(err) { - return 0, fmt.Errorf("something went wrong when fetching the managed Kubernetes config map [%w]", err) - } - - newEtag := crypto.ComputeEtag([]byte(fmt.Sprintf("%v", plainTextSecretsFromApi))) - if managedKubeConfigMap == nil { - if err := r.createInfisicalManagedKubeResource(ctx, logger, *infisicalSecret, managedConfigMapReference, plainTextSecretsFromApi, newEtag, constants.MANAGED_KUBE_RESOURCE_TYPE_CONFIG_MAP); err != nil { - return 0, fmt.Errorf("failed to create managed config map [err=%s]", err) - } - } else { - if err := r.updateInfisicalManagedConfigMap(ctx, logger, managedConfigMapReference, *managedKubeConfigMap, plainTextSecretsFromApi, newEtag); err != nil { - return 0, fmt.Errorf("failed to update managed config map [err=%s]", err) - } - } - - } - } - - return secretsCount, nil -} diff --git a/k8-operator/internal/services/infisicalsecret/reconciler.go b/k8-operator/internal/services/infisicalsecret/reconciler.go index df6c20428..1c175bae3 100644 --- a/k8-operator/internal/services/infisicalsecret/reconciler.go +++ b/k8-operator/internal/services/infisicalsecret/reconciler.go @@ -65,6 +65,7 @@ func (r *InfisicalSecretReconciler) handleAuthentication(ctx context.Context, in util.AuthStrategy.AZURE_MACHINE_IDENTITY: util.HandleAzureAuth, util.AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: util.HandleGcpIdTokenAuth, util.AuthStrategy.GCP_IAM_MACHINE_IDENTITY: util.HandleGcpIamAuth, + util.AuthStrategy.LDAP_MACHINE_IDENTITY: util.HandleLdapAuth, } for authStrategy, authHandler := range authStrategies { diff --git a/k8-operator/internal/util/kubernetes.go b/k8-operator/internal/util/kubernetes.go index a50af803b..103da63eb 100644 --- a/k8-operator/internal/util/kubernetes.go +++ b/k8-operator/internal/util/kubernetes.go @@ -18,6 +18,9 @@ import ( const INFISICAL_MACHINE_IDENTITY_CLIENT_ID = "clientId" const INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET = "clientSecret" +const INFISICAL_MACHINE_IDENTITY_LDAP_USERNAME = "username" +const INFISICAL_MACHINE_IDENTITY_LDAP_PASSWORD = "password" + func GetKubeSecretByNamespacedName(ctx context.Context, reconcilerClient client.Client, namespacedName types.NamespacedName) (*corev1.Secret, error) { kubeSecret := &corev1.Secret{} err := reconcilerClient.Get(ctx, namespacedName, kubeSecret) @@ -38,7 +41,7 @@ func GetKubeConfigMapByNamespacedName(ctx context.Context, reconcilerClient clie return kubeConfigMap, err } -func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, universalAuthRef v1alpha1.KubeSecretReference) (machineIdentityDetails model.MachineIdentityDetails, err error) { +func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, universalAuthRef v1alpha1.KubeSecretReference) (machineIdentityDetails model.UniversalAuthIdentityDetails, err error) { universalAuthCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{ Namespace: universalAuthRef.SecretNamespace, @@ -48,17 +51,39 @@ func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClie }) if k8Errors.IsNotFound(err) { - return model.MachineIdentityDetails{}, nil + return model.UniversalAuthIdentityDetails{}, nil } if err != nil { - return model.MachineIdentityDetails{}, fmt.Errorf("something went wrong when fetching your machine identity credentials [err=%s]", err) + return model.UniversalAuthIdentityDetails{}, fmt.Errorf("something went wrong when fetching your machine identity credentials [err=%s]", err) } clientIdFromSecret := universalAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_CLIENT_ID] clientSecretFromSecret := universalAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET] - return model.MachineIdentityDetails{ClientId: string(clientIdFromSecret), ClientSecret: string(clientSecretFromSecret)}, nil + return model.UniversalAuthIdentityDetails{ClientId: string(clientIdFromSecret), ClientSecret: string(clientSecretFromSecret)}, nil + +} + +func GetInfisicalLdapAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, ldapAuthRef v1alpha1.KubeSecretReference) (machineIdentityDetails model.LdapIdentityDetails, err error) { + + ldapAuthCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{ + Namespace: ldapAuthRef.SecretNamespace, + Name: ldapAuthRef.SecretName, + }) + + if k8Errors.IsNotFound(err) { + return model.LdapIdentityDetails{}, nil + } + + if err != nil { + return model.LdapIdentityDetails{}, fmt.Errorf("something went wrong when fetching your machine identity credentials [err=%s]", err) + } + + usernameFromSecret := ldapAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_LDAP_USERNAME] + passwordFromSecret := ldapAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_LDAP_PASSWORD] + + return model.LdapIdentityDetails{Username: string(usernameFromSecret), Password: string(passwordFromSecret)}, nil } diff --git a/k8-operator/packages/util/kubernetes.go b/k8-operator/packages/util/kubernetes.go deleted file mode 100644 index c4a76fa44..000000000 --- a/k8-operator/packages/util/kubernetes.go +++ /dev/null @@ -1,117 +0,0 @@ -package util - -import ( - "context" - "fmt" - - "github.com/Infisical/infisical/k8-operator/api/v1alpha1" - "github.com/Infisical/infisical/k8-operator/packages/model" - corev1 "k8s.io/api/core/v1" - k8Errors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -const INFISICAL_MACHINE_IDENTITY_CLIENT_ID = "clientId" -const INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET = "clientSecret" - -const INFISICAL_MACHINE_IDENTITY_LDAP_USERNAME = "username" -const INFISICAL_MACHINE_IDENTITY_LDAP_PASSWORD = "password" - -func GetKubeSecretByNamespacedName(ctx context.Context, reconcilerClient client.Client, namespacedName types.NamespacedName) (*corev1.Secret, error) { - kubeSecret := &corev1.Secret{} - err := reconcilerClient.Get(ctx, namespacedName, kubeSecret) - if err != nil { - kubeSecret = nil - } - - return kubeSecret, err -} - -func GetKubeConfigMapByNamespacedName(ctx context.Context, reconcilerClient client.Client, namespacedName types.NamespacedName) (*corev1.ConfigMap, error) { - kubeConfigMap := &corev1.ConfigMap{} - err := reconcilerClient.Get(ctx, namespacedName, kubeConfigMap) - if err != nil { - kubeConfigMap = nil - } - - return kubeConfigMap, err -} - -func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, universalAuthRef v1alpha1.KubeSecretReference) (machineIdentityDetails model.UniversalAuthIdentityDetails, err error) { - - universalAuthCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{ - Namespace: universalAuthRef.SecretNamespace, - Name: universalAuthRef.SecretName, - // Namespace: infisicalSecret.Spec.Authentication.UniversalAuth.CredentialsRef.SecretNamespace, - // Name: infisicalSecret.Spec.Authentication.UniversalAuth.CredentialsRef.SecretName, - }) - - if k8Errors.IsNotFound(err) { - return model.UniversalAuthIdentityDetails{}, nil - } - - if err != nil { - return model.UniversalAuthIdentityDetails{}, fmt.Errorf("something went wrong when fetching your machine identity credentials [err=%s]", err) - } - - clientIdFromSecret := universalAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_CLIENT_ID] - clientSecretFromSecret := universalAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_CLIENT_SECRET] - - return model.UniversalAuthIdentityDetails{ClientId: string(clientIdFromSecret), ClientSecret: string(clientSecretFromSecret)}, nil - -} - -func GetInfisicalLdapAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, ldapAuthRef v1alpha1.KubeSecretReference) (machineIdentityDetails model.LdapIdentityDetails, err error) { - - ldapAuthCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{ - Namespace: ldapAuthRef.SecretNamespace, - Name: ldapAuthRef.SecretName, - }) - - if k8Errors.IsNotFound(err) { - return model.LdapIdentityDetails{}, nil - } - - if err != nil { - return model.LdapIdentityDetails{}, fmt.Errorf("something went wrong when fetching your machine identity credentials [err=%s]", err) - } - - usernameFromSecret := ldapAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_LDAP_USERNAME] - passwordFromSecret := ldapAuthCredsFromKubeSecret.Data[INFISICAL_MACHINE_IDENTITY_LDAP_PASSWORD] - - return model.LdapIdentityDetails{Username: string(usernameFromSecret), Password: string(passwordFromSecret)}, nil - -} - -func getKubeClusterConfig() (*rest.Config, error) { - config, err := rest.InClusterConfig() - if err != nil { - - loadingRules := clientcmd.NewDefaultClientConfigLoadingRules() - configOverrides := &clientcmd.ConfigOverrides{} - kubeConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, configOverrides) - return kubeConfig.ClientConfig() - } - - return config, nil -} - -func GetRestClientFromClient() (rest.Interface, error) { - - config, err := getKubeClusterConfig() - if err != nil { - return nil, err - } - - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, err - } - - return clientset.CoreV1().RESTClient(), nil - -} From ef6f5ecc4ba9d891ddd1ec560a03b27ce658c1d0 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 6 Aug 2025 18:14:13 +0400 Subject: [PATCH 4/9] test --- k8-operator/go.mod | 4 +++- k8-operator/go.sum | 8 ++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/k8-operator/go.mod b/k8-operator/go.mod index a80b8839f..3c5150399 100644 --- a/k8-operator/go.mod +++ b/k8-operator/go.mod @@ -16,12 +16,14 @@ require ( golang.org/x/crypto v0.36.0 gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.33.0 - k8s.io/apimachinery v0.33.0 + k8s.io/apimachinery v0.33.3 k8s.io/client-go v0.33.0 sigs.k8s.io/controller-runtime v0.21.0 software.sslmate.com/src/go-pkcs12 v0.6.0 ) +replace github.com/google/go-cmp v0.7.0 => github.com/google/go-cmp v0.6.0 + require ( cel.dev/expr v0.19.1 // indirect cloud.google.com/go/auth v0.7.0 // indirect diff --git a/k8-operator/go.sum b/k8-operator/go.sum index 3434d5971..581bad7ac 100644 --- a/k8-operator/go.sum +++ b/k8-operator/go.sum @@ -136,8 +136,8 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -431,8 +431,8 @@ k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU= k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM= k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs= k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc= -k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ= -k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= +k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= k8s.io/apiserver v0.33.0 h1:QqcM6c+qEEjkOODHppFXRiw/cE2zP85704YrQ9YaBbc= k8s.io/apiserver v0.33.0/go.mod h1:EixYOit0YTxt8zrO2kBU7ixAtxFce9gKGq367nFmqI8= k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98= From e694293ebe2c5beeec2dbb7f5052a1fe692515f7 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 6 Aug 2025 18:17:28 +0400 Subject: [PATCH 5/9] update deps --- k8-operator/go.mod | 4 ++-- k8-operator/go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/k8-operator/go.mod b/k8-operator/go.mod index 3c5150399..7d0e51463 100644 --- a/k8-operator/go.mod +++ b/k8-operator/go.mod @@ -15,9 +15,9 @@ require ( github.com/sethvargo/go-password v0.3.1 golang.org/x/crypto v0.36.0 gopkg.in/yaml.v3 v3.0.1 - k8s.io/api v0.33.0 + k8s.io/api v0.33.3 k8s.io/apimachinery v0.33.3 - k8s.io/client-go v0.33.0 + k8s.io/client-go v0.33.3 sigs.k8s.io/controller-runtime v0.21.0 software.sslmate.com/src/go-pkcs12 v0.6.0 ) diff --git a/k8-operator/go.sum b/k8-operator/go.sum index 581bad7ac..3b4a64824 100644 --- a/k8-operator/go.sum +++ b/k8-operator/go.sum @@ -427,16 +427,16 @@ 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-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU= -k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM= +k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= +k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE= k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs= k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc= k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= k8s.io/apiserver v0.33.0 h1:QqcM6c+qEEjkOODHppFXRiw/cE2zP85704YrQ9YaBbc= k8s.io/apiserver v0.33.0/go.mod h1:EixYOit0YTxt8zrO2kBU7ixAtxFce9gKGq367nFmqI8= -k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98= -k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg= +k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA= +k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= k8s.io/component-base v0.33.0 h1:Ot4PyJI+0JAD9covDhwLp9UNkUja209OzsJ4FzScBNk= k8s.io/component-base v0.33.0/go.mod h1:aXYZLbw3kihdkOPMDhWbjGCO6sg+luw554KP51t8qCU= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= From 2d68f9aa16a23f48e832c0088af448e55d04a478 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Wed, 6 Aug 2025 18:29:19 +0400 Subject: [PATCH 6/9] fix: helm changes --- .../workflows/release_docker_k8_operator.yaml | 5 +- .../templates/clustergenerator-crd.yaml | 41 ++-- .../templates/deployment.yaml | 42 +--- .../infisicaldynamicsecret-admin-rbac.yaml | 49 +++++ .../templates/infisicaldynamicsecret-crd.yaml | 91 ++++---- .../infisicaldynamicsecret-editor-rbac.yaml | 55 +++++ .../infisicaldynamicsecret-viewer-rbac.yaml | 51 +++++ .../templates/infisicalpushsecret-crd.yaml | 81 ++++--- .../infisicalpushsecretsecret-admin-rbac.yaml | 49 +++++ ...infisicalpushsecretsecret-editor-rbac.yaml | 55 +++++ ...infisicalpushsecretsecret-viewer-rbac.yaml | 51 +++++ .../templates/infisicalsecret-admin-rbac.yaml | 49 +++++ .../templates/infisicalsecret-crd.yaml | 124 +++++------ .../infisicalsecret-editor-rbac.yaml | 55 +++++ .../infisicalsecret-viewer-rbac.yaml | 51 +++++ .../templates/leader-election-rbac.yaml | 6 - .../templates/manager-rbac.yaml | 92 +------- .../templates/metrics-auth-rbac.yaml | 53 +++++ .../templates/metrics-reader-rbac.yaml | 3 - .../templates/metrics-service.yaml | 4 +- .../templates/proxy-rbac.yaml | 43 ---- .../templates/serviceaccount.yaml | 3 - helm-charts/secrets-operator/values.yaml | 30 +-- k8-operator/Makefile | 25 ++- k8-operator/config/manager/manager.yaml | 72 +++---- k8-operator/scripts/generate-helm.sh | 199 +++++++++++------- 26 files changed, 873 insertions(+), 506 deletions(-) create mode 100644 helm-charts/secrets-operator/templates/infisicaldynamicsecret-admin-rbac.yaml create mode 100644 helm-charts/secrets-operator/templates/infisicaldynamicsecret-editor-rbac.yaml create mode 100644 helm-charts/secrets-operator/templates/infisicaldynamicsecret-viewer-rbac.yaml create mode 100644 helm-charts/secrets-operator/templates/infisicalpushsecretsecret-admin-rbac.yaml create mode 100644 helm-charts/secrets-operator/templates/infisicalpushsecretsecret-editor-rbac.yaml create mode 100644 helm-charts/secrets-operator/templates/infisicalpushsecretsecret-viewer-rbac.yaml create mode 100644 helm-charts/secrets-operator/templates/infisicalsecret-admin-rbac.yaml create mode 100644 helm-charts/secrets-operator/templates/infisicalsecret-editor-rbac.yaml create mode 100644 helm-charts/secrets-operator/templates/infisicalsecret-viewer-rbac.yaml create mode 100644 helm-charts/secrets-operator/templates/metrics-auth-rbac.yaml delete mode 100644 helm-charts/secrets-operator/templates/proxy-rbac.yaml diff --git a/.github/workflows/release_docker_k8_operator.yaml b/.github/workflows/release_docker_k8_operator.yaml index 1f894df47..81472bf88 100644 --- a/.github/workflows/release_docker_k8_operator.yaml +++ b/.github/workflows/release_docker_k8_operator.yaml @@ -44,10 +44,7 @@ jobs: - name: Generate Helm Chart working-directory: k8-operator - run: make helm - - - name: Update Helm Chart Version - run: ./k8-operator/scripts/update-version.sh ${{ steps.extract_version.outputs.version }} + run: make helm VERSION=${{ steps.extract_version.outputs.version }} - name: Debug - Check file changes run: | diff --git a/helm-charts/secrets-operator/templates/clustergenerator-crd.yaml b/helm-charts/secrets-operator/templates/clustergenerator-crd.yaml index 8da166a5e..2085502c9 100644 --- a/helm-charts/secrets-operator/templates/clustergenerator-crd.yaml +++ b/helm-charts/secrets-operator/templates/clustergenerator-crd.yaml @@ -4,7 +4,7 @@ kind: CustomResourceDefinition metadata: name: clustergenerators.secrets.infisical.com annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.18.0 labels: {{- include "secrets-operator.labels" . | nindent 4 }} spec: @@ -22,14 +22,19 @@ spec: description: ClusterGenerator represents a cluster-wide generator 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' + 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' + 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 @@ -47,27 +52,29 @@ spec: description: set allowRepeat to true to allow repeating characters. type: boolean digits: - description: digits specifies the number of digits in the generated - password. If omitted it defaults to 25% of the length of the - password + description: |- + digits specifies the number of digits in the generated + password. If omitted it defaults to 25% of the length of the password type: integer length: default: 24 - description: Length of the password to be generated. Defaults - to 24 + description: |- + Length of the password to be generated. + Defaults to 24 type: integer noUpper: default: false description: Set noUpper to disable uppercase characters type: boolean symbolCharacters: - description: symbolCharacters specifies the special characters - that should be used in the generated password. + description: |- + symbolCharacters specifies the special characters that should be used + in the generated password. type: string symbols: - description: symbols specifies the number of symbol characters - in the generated password. If omitted it defaults to 25% of - the length of the password + description: |- + symbols specifies the number of symbol characters in the generated + password. If omitted it defaults to 25% of the length of the password type: integer type: object uuidSpec: diff --git a/helm-charts/secrets-operator/templates/deployment.yaml b/helm-charts/secrets-operator/templates/deployment.yaml index 8a99e104e..ca7b16401 100644 --- a/helm-charts/secrets-operator/templates/deployment.yaml +++ b/helm-charts/secrets-operator/templates/deployment.yaml @@ -3,62 +3,26 @@ kind: Deployment metadata: name: {{ include "secrets-operator.fullname" . }}-controller-manager labels: - app.kubernetes.io/component: manager - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator control-plane: controller-manager {{- include "secrets-operator.labels" . | nindent 4 }} spec: replicas: {{ .Values.controllerManager.replicas }} selector: matchLabels: + app.kubernetes.io/name: k8-operator control-plane: controller-manager {{- include "secrets-operator.selectorLabels" . | nindent 6 }} template: metadata: labels: + app.kubernetes.io/name: k8-operator control-plane: controller-manager {{- include "secrets-operator.selectorLabels" . | nindent 8 }} annotations: kubectl.kubernetes.io/default-container: manager spec: - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/arch - operator: In - values: - - amd64 - - arm64 - - ppc64le - - s390x - - key: kubernetes.io/os - operator: In - values: - - linux containers: - - args: {{- toYaml .Values.controllerManager.kubeRbacProxy.args | nindent 8 }} - env: - - name: KUBERNETES_CLUSTER_DOMAIN - value: {{ quote .Values.kubernetesClusterDomain }} - image: {{ .Values.controllerManager.kubeRbacProxy.image.repository }}:{{ .Values.controllerManager.kubeRbacProxy.image.tag - | default .Chart.AppVersion }} - name: kube-rbac-proxy - ports: - - containerPort: 8443 - name: https - protocol: TCP - resources: {{- toYaml .Values.controllerManager.kubeRbacProxy.resources | nindent - 10 }} - securityContext: {{- toYaml .Values.controllerManager.kubeRbacProxy.containerSecurityContext - | nindent 10 }} - - args: - {{- toYaml .Values.controllerManager.manager.args | nindent 8 }} - {{- if and .Values.scopedNamespace .Values.scopedRBAC }} - - --namespace={{ .Values.scopedNamespace }} - {{- end }} + - args: {{- toYaml .Values.controllerManager.manager.args | nindent 8 }} command: - /manager env: diff --git a/helm-charts/secrets-operator/templates/infisicaldynamicsecret-admin-rbac.yaml b/helm-charts/secrets-operator/templates/infisicaldynamicsecret-admin-rbac.yaml new file mode 100644 index 000000000..1e8e0fb22 --- /dev/null +++ b/helm-charts/secrets-operator/templates/infisicaldynamicsecret-admin-rbac.yaml @@ -0,0 +1,49 @@ +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: Role +{{- else }} +kind: ClusterRole +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicaldynamicsecret-admin-role + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + {{- include "secrets-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets + verbs: + - '*' +- apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: RoleBinding +{{- else }} +kind: ClusterRoleBinding +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicaldynamicsecret-admin-rolebinding + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + + {{- include "secrets-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + kind: Role + {{- else }} + kind: ClusterRole + {{- end }} + name: '{{ include "secrets-operator.fullname" . }}-infisicaldynamicsecret-admin-role' diff --git a/helm-charts/secrets-operator/templates/infisicaldynamicsecret-crd.yaml b/helm-charts/secrets-operator/templates/infisicaldynamicsecret-crd.yaml index 179305957..fe1ab4481 100644 --- a/helm-charts/secrets-operator/templates/infisicaldynamicsecret-crd.yaml +++ b/helm-charts/secrets-operator/templates/infisicaldynamicsecret-crd.yaml @@ -4,7 +4,7 @@ kind: CustomResourceDefinition metadata: name: infisicaldynamicsecrets.secrets.infisical.com annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.18.0 labels: {{- include "secrets-operator.labels" . | nindent 4 }} spec: @@ -23,14 +23,19 @@ spec: 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' + 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' + 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 @@ -75,11 +80,9 @@ spec: kubernetesAuth: properties: autoCreateServiceAccountToken: - description: Optionally automatically create a service account - token for the configured service account. If this is set to - `true`, the operator will automatically create a service account - token for the configured service account. This field is recommended - in most cases. + description: |- + Optionally automatically create a service account token for the configured service account. + If this is set to `true`, the operator will automatically create a service account token for the configured service account. This field is recommended in most cases. type: boolean identityId: type: string @@ -170,11 +173,11 @@ spec: properties: creationPolicy: default: Orphan - description: 'The Kubernetes Secret creation policy. Enum with values: - ''Owner'', ''Orphan''. Owner creates the secret and sets .metadata.ownerReferences - of the InfisicalSecret CRD that created it. Orphan will not set - the secret owner. This will result in the secret being orphaned - and not deleted when the resource is deleted.' + description: |- + The Kubernetes Secret creation policy. + Enum with values: 'Owner', 'Orphan'. + Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. + Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted. type: string secretName: description: The name of the Kubernetes Secret @@ -196,9 +199,9 @@ spec: description: The template key values type: object includeAllSecrets: - description: This injects all retrieved secrets into the top - level of your template. Secrets defined in the template will - take precedence over the injected ones. + description: |- + This injects all retrieved secrets into the top level of your template. + Secrets defined in the template will take precedence over the injected ones. type: boolean type: object required: @@ -239,44 +242,36 @@ spec: properties: conditions: items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a foo's - current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details - about the transition. This may be an empty string. + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers of - specific condition types may define expected values and meanings - for this field, and whether the values are considered a guaranteed - API. The value should be a CamelCase string. This field may - not be empty. + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ @@ -290,10 +285,6 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/helm-charts/secrets-operator/templates/infisicaldynamicsecret-editor-rbac.yaml b/helm-charts/secrets-operator/templates/infisicaldynamicsecret-editor-rbac.yaml new file mode 100644 index 000000000..117f9aa1a --- /dev/null +++ b/helm-charts/secrets-operator/templates/infisicaldynamicsecret-editor-rbac.yaml @@ -0,0 +1,55 @@ +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: Role +{{- else }} +kind: ClusterRole +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicaldynamicsecret-editor-role + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + {{- include "secrets-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: RoleBinding +{{- else }} +kind: ClusterRoleBinding +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicaldynamicsecret-editor-rolebinding + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + + {{- include "secrets-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + kind: Role + {{- else }} + kind: ClusterRole + {{- end }} + name: '{{ include "secrets-operator.fullname" . }}-infisicaldynamicsecret-editor-role' diff --git a/helm-charts/secrets-operator/templates/infisicaldynamicsecret-viewer-rbac.yaml b/helm-charts/secrets-operator/templates/infisicaldynamicsecret-viewer-rbac.yaml new file mode 100644 index 000000000..3df918d21 --- /dev/null +++ b/helm-charts/secrets-operator/templates/infisicaldynamicsecret-viewer-rbac.yaml @@ -0,0 +1,51 @@ +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: Role +{{- else }} +kind: ClusterRole +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicaldynamicsecret-viewer-role + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + {{- include "secrets-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets + verbs: + - get + - list + - watch +- apiGroups: + - secrets.infisical.com + resources: + - infisicaldynamicsecrets/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: RoleBinding +{{- else }} +kind: ClusterRoleBinding +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicaldynamicsecret-viewer-rolebinding + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + + {{- include "secrets-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + kind: Role + {{- else }} + kind: ClusterRole + {{- end }} + name: '{{ include "secrets-operator.fullname" . }}-infisicaldynamicsecret-viewer-role' diff --git a/helm-charts/secrets-operator/templates/infisicalpushsecret-crd.yaml b/helm-charts/secrets-operator/templates/infisicalpushsecret-crd.yaml index 8c8091d8a..2738ef581 100644 --- a/helm-charts/secrets-operator/templates/infisicalpushsecret-crd.yaml +++ b/helm-charts/secrets-operator/templates/infisicalpushsecret-crd.yaml @@ -4,7 +4,7 @@ kind: CustomResourceDefinition metadata: name: infisicalpushsecrets.secrets.infisical.com annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.18.0 labels: {{- include "secrets-operator.labels" . | nindent 4 }} spec: @@ -23,14 +23,19 @@ spec: 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' + 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' + 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 @@ -75,11 +80,9 @@ spec: kubernetesAuth: properties: autoCreateServiceAccountToken: - description: Optionally automatically create a service account - token for the configured service account. If this is set to - `true`, the operator will automatically create a service account - token for the configured service account. This field is recommended - in most cases. + description: |- + Optionally automatically create a service account token for the configured service account. + If this is set to `true`, the operator will automatically create a service account token for the configured service account. This field is recommended in most cases. type: boolean identityId: type: string @@ -208,9 +211,9 @@ spec: description: The template key values type: object includeAllSecrets: - description: This injects all retrieved secrets into the - top level of your template. Secrets defined in the template - will take precedence over the injected ones. + description: |- + This injects all retrieved secrets into the top level of your template. + Secrets defined in the template will take precedence over the injected ones. type: boolean type: object required: @@ -252,44 +255,36 @@ spec: properties: conditions: items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a foo's - current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details - about the transition. This may be an empty string. + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers of - specific condition types may define expected values and meanings - for this field, and whether the values are considered a guaranteed - API. The value should be a CamelCase string. This field may - not be empty. + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ @@ -303,10 +298,6 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/helm-charts/secrets-operator/templates/infisicalpushsecretsecret-admin-rbac.yaml b/helm-charts/secrets-operator/templates/infisicalpushsecretsecret-admin-rbac.yaml new file mode 100644 index 000000000..6bc381e02 --- /dev/null +++ b/helm-charts/secrets-operator/templates/infisicalpushsecretsecret-admin-rbac.yaml @@ -0,0 +1,49 @@ +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: Role +{{- else }} +kind: ClusterRole +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicalpushsecretsecret-admin-role + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + {{- include "secrets-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicalpushsecretsecrets + verbs: + - '*' +- apiGroups: + - secrets.infisical.com + resources: + - infisicalpushsecretsecrets/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: RoleBinding +{{- else }} +kind: ClusterRoleBinding +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicalpushsecretsecret-admin-rolebinding + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + + {{- include "secrets-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + kind: Role + {{- else }} + kind: ClusterRole + {{- end }} + name: '{{ include "secrets-operator.fullname" . }}-infisicalpushsecretsecret-admin-role' diff --git a/helm-charts/secrets-operator/templates/infisicalpushsecretsecret-editor-rbac.yaml b/helm-charts/secrets-operator/templates/infisicalpushsecretsecret-editor-rbac.yaml new file mode 100644 index 000000000..b279cf179 --- /dev/null +++ b/helm-charts/secrets-operator/templates/infisicalpushsecretsecret-editor-rbac.yaml @@ -0,0 +1,55 @@ +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: Role +{{- else }} +kind: ClusterRole +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicalpushsecretsecret-editor-role + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + {{- include "secrets-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicalpushsecretsecrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - secrets.infisical.com + resources: + - infisicalpushsecretsecrets/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: RoleBinding +{{- else }} +kind: ClusterRoleBinding +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicalpushsecretsecret-editor-rolebinding + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + + {{- include "secrets-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + kind: Role + {{- else }} + kind: ClusterRole + {{- end }} + name: '{{ include "secrets-operator.fullname" . }}-infisicalpushsecretsecret-editor-role' diff --git a/helm-charts/secrets-operator/templates/infisicalpushsecretsecret-viewer-rbac.yaml b/helm-charts/secrets-operator/templates/infisicalpushsecretsecret-viewer-rbac.yaml new file mode 100644 index 000000000..12fea8635 --- /dev/null +++ b/helm-charts/secrets-operator/templates/infisicalpushsecretsecret-viewer-rbac.yaml @@ -0,0 +1,51 @@ +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: Role +{{- else }} +kind: ClusterRole +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicalpushsecretsecret-viewer-role + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + {{- include "secrets-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicalpushsecretsecrets + verbs: + - get + - list + - watch +- apiGroups: + - secrets.infisical.com + resources: + - infisicalpushsecretsecrets/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: RoleBinding +{{- else }} +kind: ClusterRoleBinding +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicalpushsecretsecret-viewer-rolebinding + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + + {{- include "secrets-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + kind: Role + {{- else }} + kind: ClusterRole + {{- end }} + name: '{{ include "secrets-operator.fullname" . }}-infisicalpushsecretsecret-viewer-role' diff --git a/helm-charts/secrets-operator/templates/infisicalsecret-admin-rbac.yaml b/helm-charts/secrets-operator/templates/infisicalsecret-admin-rbac.yaml new file mode 100644 index 000000000..1016dbe56 --- /dev/null +++ b/helm-charts/secrets-operator/templates/infisicalsecret-admin-rbac.yaml @@ -0,0 +1,49 @@ +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: Role +{{- else }} +kind: ClusterRole +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicalsecret-admin-role + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + {{- include "secrets-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicalsecrets + verbs: + - '*' +- apiGroups: + - secrets.infisical.com + resources: + - infisicalsecrets/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: RoleBinding +{{- else }} +kind: ClusterRoleBinding +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicalsecret-admin-rolebinding + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + + {{- include "secrets-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + kind: Role + {{- else }} + kind: ClusterRole + {{- end }} + name: '{{ include "secrets-operator.fullname" . }}-infisicalsecret-admin-role' diff --git a/helm-charts/secrets-operator/templates/infisicalsecret-crd.yaml b/helm-charts/secrets-operator/templates/infisicalsecret-crd.yaml index 6002bbe36..117197686 100644 --- a/helm-charts/secrets-operator/templates/infisicalsecret-crd.yaml +++ b/helm-charts/secrets-operator/templates/infisicalsecret-crd.yaml @@ -4,7 +4,7 @@ kind: CustomResourceDefinition metadata: name: infisicalsecrets.secrets.infisical.com annotations: - controller-gen.kubebuilder.io/version: v0.10.0 + controller-gen.kubebuilder.io/version: v0.18.0 labels: {{- include "secrets-operator.labels" . | nindent 4 }} spec: @@ -22,14 +22,19 @@ spec: description: InfisicalSecret is the Schema for the infisicalsecrets 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' + 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' + 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 @@ -138,10 +143,9 @@ spec: kubernetesAuth: properties: autoCreateServiceAccountToken: - description: Optionally automatically create a service account - token for the configured service account. If this is set to - `true`, the operator will automatically create a service account - token for the configured service account. + description: |- + Optionally automatically create a service account token for the configured service account. + If this is set to `true`, the operator will automatically create a service account token for the configured service account. type: boolean identityId: type: string @@ -323,12 +327,11 @@ spec: type: string creationPolicy: default: Orphan - description: 'The Kubernetes ConfigMap creation policy. Enum with - values: ''Owner'', ''Orphan''. Owner creates the config map - and sets .metadata.ownerReferences of the InfisicalSecret CRD - that created it. Orphan will not set the config map owner. This - will result in the config map being orphaned and not deleted - when the resource is deleted.' + description: |- + The Kubernetes ConfigMap creation policy. + Enum with values: 'Owner', 'Orphan'. + Owner creates the config map and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. + Orphan will not set the config map owner. This will result in the config map being orphaned and not deleted when the resource is deleted. type: string template: description: The template to transform the secret data @@ -339,9 +342,9 @@ spec: description: The template key values type: object includeAllSecrets: - description: This injects all retrieved secrets into the top - level of your template. Secrets defined in the template - will take precedence over the injected ones. + description: |- + This injects all retrieved secrets into the top level of your template. + Secrets defined in the template will take precedence over the injected ones. type: boolean type: object required: @@ -354,12 +357,11 @@ spec: properties: creationPolicy: default: Orphan - description: 'The Kubernetes Secret creation policy. Enum with - values: ''Owner'', ''Orphan''. Owner creates the secret and - sets .metadata.ownerReferences of the InfisicalSecret CRD that - created it. Orphan will not set the secret owner. This will - result in the secret being orphaned and not deleted when the - resource is deleted.' + description: |- + The Kubernetes Secret creation policy. + Enum with values: 'Owner', 'Orphan'. + Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. + Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted. type: string secretName: description: The name of the Kubernetes Secret @@ -381,9 +383,9 @@ spec: description: The template key values type: object includeAllSecrets: - description: This injects all retrieved secrets into the top - level of your template. Secrets defined in the template - will take precedence over the injected ones. + description: |- + This injects all retrieved secrets into the top level of your template. + Secrets defined in the template will take precedence over the injected ones. type: boolean type: object required: @@ -395,11 +397,11 @@ spec: properties: creationPolicy: default: Orphan - description: 'The Kubernetes Secret creation policy. Enum with values: - ''Owner'', ''Orphan''. Owner creates the secret and sets .metadata.ownerReferences - of the InfisicalSecret CRD that created it. Orphan will not set - the secret owner. This will result in the secret being orphaned - and not deleted when the resource is deleted.' + description: |- + The Kubernetes Secret creation policy. + Enum with values: 'Owner', 'Orphan'. + Owner creates the secret and sets .metadata.ownerReferences of the InfisicalSecret CRD that created it. + Orphan will not set the secret owner. This will result in the secret being orphaned and not deleted when the resource is deleted. type: string secretName: description: The name of the Kubernetes Secret @@ -421,9 +423,9 @@ spec: description: The template key values type: object includeAllSecrets: - description: This injects all retrieved secrets into the top - level of your template. Secrets defined in the template will - take precedence over the injected ones. + description: |- + This injects all retrieved secrets into the top level of your template. + Secrets defined in the template will take precedence over the injected ones. type: boolean type: object required: @@ -474,44 +476,36 @@ spec: properties: conditions: items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - \n type FooStatus struct{ // Represents the observations of a foo's - current state. // Known .status.conditions.type are: \"Available\", - \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge - // +listType=map // +listMapKey=type Conditions []metav1.Condition - `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" - protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }" + description: Condition contains details for one aspect of the current + state of this API Resource. properties: lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. format: date-time type: string message: - description: message is a human readable message indicating details - about the transition. This may be an empty string. + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. maxLength: 32768 type: string observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. format: int64 minimum: 0 type: integer reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers of - specific condition types may define expected values and meanings - for this field, and whether the values are considered a guaranteed - API. The value should be a CamelCase string. This field may - not be empty. + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. maxLength: 1024 minLength: 1 pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ @@ -525,10 +519,6 @@ spec: type: string type: description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) maxLength: 316 pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ type: string diff --git a/helm-charts/secrets-operator/templates/infisicalsecret-editor-rbac.yaml b/helm-charts/secrets-operator/templates/infisicalsecret-editor-rbac.yaml new file mode 100644 index 000000000..6f74acba1 --- /dev/null +++ b/helm-charts/secrets-operator/templates/infisicalsecret-editor-rbac.yaml @@ -0,0 +1,55 @@ +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: Role +{{- else }} +kind: ClusterRole +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicalsecret-editor-role + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + {{- include "secrets-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicalsecrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - secrets.infisical.com + resources: + - infisicalsecrets/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: RoleBinding +{{- else }} +kind: ClusterRoleBinding +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicalsecret-editor-rolebinding + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + + {{- include "secrets-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + kind: Role + {{- else }} + kind: ClusterRole + {{- end }} + name: '{{ include "secrets-operator.fullname" . }}-infisicalsecret-editor-role' diff --git a/helm-charts/secrets-operator/templates/infisicalsecret-viewer-rbac.yaml b/helm-charts/secrets-operator/templates/infisicalsecret-viewer-rbac.yaml new file mode 100644 index 000000000..2f63b44ef --- /dev/null +++ b/helm-charts/secrets-operator/templates/infisicalsecret-viewer-rbac.yaml @@ -0,0 +1,51 @@ +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: Role +{{- else }} +kind: ClusterRole +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicalsecret-viewer-role + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + {{- include "secrets-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - secrets.infisical.com + resources: + - infisicalsecrets + verbs: + - get + - list + - watch +- apiGroups: + - secrets.infisical.com + resources: + - infisicalsecrets/status + verbs: + - get +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: RoleBinding +{{- else }} +kind: ClusterRoleBinding +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-infisicalsecret-viewer-rolebinding + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + + {{- include "secrets-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + kind: Role + {{- else }} + kind: ClusterRole + {{- end }} + name: '{{ include "secrets-operator.fullname" . }}-infisicalsecret-viewer-role' diff --git a/helm-charts/secrets-operator/templates/leader-election-rbac.yaml b/helm-charts/secrets-operator/templates/leader-election-rbac.yaml index 8299d35f6..b4ecb9357 100644 --- a/helm-charts/secrets-operator/templates/leader-election-rbac.yaml +++ b/helm-charts/secrets-operator/templates/leader-election-rbac.yaml @@ -3,9 +3,6 @@ kind: Role metadata: name: {{ include "secrets-operator.fullname" . }}-leader-election-role labels: - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator {{- include "secrets-operator.labels" . | nindent 4 }} rules: - apiGroups: @@ -45,9 +42,6 @@ kind: RoleBinding metadata: name: {{ include "secrets-operator.fullname" . }}-leader-election-rolebinding labels: - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator {{- include "secrets-operator.labels" . | nindent 4 }} roleRef: apiGroup: rbac.authorization.k8s.io diff --git a/helm-charts/secrets-operator/templates/manager-rbac.yaml b/helm-charts/secrets-operator/templates/manager-rbac.yaml index 93289ca67..9cd79264a 100644 --- a/helm-charts/secrets-operator/templates/manager-rbac.yaml +++ b/helm-charts/secrets-operator/templates/manager-rbac.yaml @@ -16,6 +16,7 @@ rules: - "" resources: - configmaps + - secrets verbs: - create - delete @@ -30,17 +31,6 @@ rules: verbs: - get - list -- apiGroups: - - "" - resources: - - secrets - verbs: - - create - - delete - - get - - list - - update - - watch - apiGroups: - "" resources: @@ -55,17 +45,6 @@ rules: - serviceaccounts/token verbs: - create -- apiGroups: - - apps - resources: - - daemonsets - - deployments - - statefulsets - verbs: - - get - - list - - update - - watch - apiGroups: - apps resources: @@ -85,69 +64,8 @@ rules: - secrets.infisical.com resources: - clustergenerators - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - secrets.infisical.com - resources: - infisicaldynamicsecrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - secrets.infisical.com - resources: - - infisicaldynamicsecrets/finalizers - verbs: - - update -- apiGroups: - - secrets.infisical.com - resources: - - infisicaldynamicsecrets/status - verbs: - - get - - patch - - update -- apiGroups: - - secrets.infisical.com - resources: - infisicalpushsecrets - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - secrets.infisical.com - resources: - - infisicalpushsecrets/finalizers - verbs: - - update -- apiGroups: - - secrets.infisical.com - resources: - - infisicalpushsecrets/status - verbs: - - get - - patch - - update -- apiGroups: - - secrets.infisical.com - resources: - infisicalsecrets verbs: - create @@ -160,12 +78,16 @@ rules: - apiGroups: - secrets.infisical.com resources: + - infisicaldynamicsecrets/finalizers + - infisicalpushsecrets/finalizers - infisicalsecrets/finalizers verbs: - update - apiGroups: - secrets.infisical.com resources: + - infisicaldynamicsecrets/status + - infisicalpushsecrets/status - infisicalsecrets/status verbs: - get @@ -184,9 +106,7 @@ metadata: namespace: {{ .Values.scopedNamespace | quote }} {{- end }} labels: - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator + {{- include "secrets-operator.labels" . | nindent 4 }} roleRef: apiGroup: rbac.authorization.k8s.io diff --git a/helm-charts/secrets-operator/templates/metrics-auth-rbac.yaml b/helm-charts/secrets-operator/templates/metrics-auth-rbac.yaml new file mode 100644 index 000000000..e5638b4e2 --- /dev/null +++ b/helm-charts/secrets-operator/templates/metrics-auth-rbac.yaml @@ -0,0 +1,53 @@ +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: Role +{{- else }} +kind: ClusterRole +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-metrics-auth-role + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + {{- include "secrets-operator.labels" . | nindent 4 }} +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create +--- +apiVersion: rbac.authorization.k8s.io/v1 +{{- if and .Values.scopedNamespace .Values.scopedRBAC }} +kind: RoleBinding +{{- else }} +kind: ClusterRoleBinding +{{- end }} +metadata: + name: {{ include "secrets-operator.fullname" . }}-metrics-auth-rolebinding + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + namespace: {{ .Values.scopedNamespace | quote }} + {{- end }} + labels: + + {{- include "secrets-operator.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + kind: Role + {{- else }} + kind: ClusterRole + {{- end }} + name: '{{ include "secrets-operator.fullname" . }}-metrics-auth-role' +subjects: +- kind: ServiceAccount + name: '{{ include "secrets-operator.fullname" . }}-controller-manager' + namespace: '{{ .Release.Namespace }}' diff --git a/helm-charts/secrets-operator/templates/metrics-reader-rbac.yaml b/helm-charts/secrets-operator/templates/metrics-reader-rbac.yaml index 7843dac3d..c9eb371b0 100644 --- a/helm-charts/secrets-operator/templates/metrics-reader-rbac.yaml +++ b/helm-charts/secrets-operator/templates/metrics-reader-rbac.yaml @@ -4,9 +4,6 @@ kind: ClusterRole metadata: name: {{ include "secrets-operator.fullname" . }}-metrics-reader labels: - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator {{- include "secrets-operator.labels" . | nindent 4 }} rules: - nonResourceURLs: diff --git a/helm-charts/secrets-operator/templates/metrics-service.yaml b/helm-charts/secrets-operator/templates/metrics-service.yaml index fab9523cf..833117d05 100644 --- a/helm-charts/secrets-operator/templates/metrics-service.yaml +++ b/helm-charts/secrets-operator/templates/metrics-service.yaml @@ -3,14 +3,12 @@ kind: Service metadata: name: {{ include "secrets-operator.fullname" . }}-controller-manager-metrics-service labels: - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator control-plane: controller-manager {{- include "secrets-operator.labels" . | nindent 4 }} spec: type: {{ .Values.metricsService.type }} selector: + app.kubernetes.io/name: k8-operator control-plane: controller-manager {{- include "secrets-operator.selectorLabels" . | nindent 4 }} ports: diff --git a/helm-charts/secrets-operator/templates/proxy-rbac.yaml b/helm-charts/secrets-operator/templates/proxy-rbac.yaml deleted file mode 100644 index cc23b0856..000000000 --- a/helm-charts/secrets-operator/templates/proxy-rbac.yaml +++ /dev/null @@ -1,43 +0,0 @@ -{{- if not .Values.scopedNamespace }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: {{ include "secrets-operator.fullname" . }}-proxy-role - labels: - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator - {{- include "secrets-operator.labels" . | nindent 4 }} -rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: {{ include "secrets-operator.fullname" . }}-proxy-rolebinding - labels: - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator - {{- include "secrets-operator.labels" . | nindent 4 }} -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: '{{ include "secrets-operator.fullname" . }}-proxy-role' -subjects: -- kind: ServiceAccount - name: '{{ include "secrets-operator.fullname" . }}-controller-manager' - namespace: '{{ .Release.Namespace }}' - -{{- end }} diff --git a/helm-charts/secrets-operator/templates/serviceaccount.yaml b/helm-charts/secrets-operator/templates/serviceaccount.yaml index 57af4c162..70443c366 100644 --- a/helm-charts/secrets-operator/templates/serviceaccount.yaml +++ b/helm-charts/secrets-operator/templates/serviceaccount.yaml @@ -3,9 +3,6 @@ kind: ServiceAccount metadata: name: {{ include "secrets-operator.fullname" . }}-controller-manager labels: - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: k8-operator - app.kubernetes.io/part-of: k8-operator {{- include "secrets-operator.labels" . | nindent 4 }} annotations: {{- toYaml .Values.controllerManager.serviceAccount.annotations | nindent 4 }} diff --git a/helm-charts/secrets-operator/values.yaml b/helm-charts/secrets-operator/values.yaml index 1f56c666c..896c93f60 100644 --- a/helm-charts/secrets-operator/values.yaml +++ b/helm-charts/secrets-operator/values.yaml @@ -1,35 +1,15 @@ controllerManager: - kubeRbacProxy: - args: - - --secure-listen-address=0.0.0.0:8443 - - --upstream=http://127.0.0.1:8080/ - - --logtostderr=true - - --v=0 - containerSecurityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - image: - repository: gcr.io/kubebuilder/kube-rbac-proxy - tag: v0.15.0 - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi manager: args: - - --health-probe-bind-address=:8081 - - --metrics-bind-address=127.0.0.1:8080 + - --metrics-bind-address=:8443 - --leader-elect + - --health-probe-bind-address=:8081 containerSecurityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL + readOnlyRootFilesystem: true image: repository: infisical/kubernetes-operator tag: v0.9.5 @@ -40,6 +20,8 @@ controllerManager: requests: cpu: 10m memory: 64Mi + seccompProfile: + type: RuntimeDefault replicas: 1 serviceAccount: annotations: {} @@ -50,7 +32,7 @@ metricsService: - name: https port: 8443 protocol: TCP - targetPort: https + targetPort: 8443 type: ClusterIP kubernetesClusterDomain: cluster.local scopedNamespace: "" diff --git a/k8-operator/Makefile b/k8-operator/Makefile index b776cf7a9..463b1537c 100644 --- a/k8-operator/Makefile +++ b/k8-operator/Makefile @@ -1,5 +1,6 @@ # Image URL to use all building/pushing image targets -IMG ?= controller:latest +VERSION ?= latest +IMG ?= infisical/kubernetes-operator:${VERSION} # ${VERSION} will be replaced by the version in the CI step # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -24,6 +25,28 @@ all: build ##@ General +HELMIFY ?= $(LOCALBIN)/helmify + +.PHONY: helmify +helmify: $(HELMIFY) ## Download helmify locally if necessary. +$(HELMIFY): $(LOCALBIN) + test -s $(LOCALBIN)/helmify || GOBIN=$(LOCALBIN) go install github.com/arttor/helmify/cmd/helmify@latest + +legacy-helm: manifests kustomize helmify + $(KUSTOMIZE) build config/default | $(HELMIFY) ../helm-charts/secrets-operator + +helm: manifests kustomize helmify + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + ./scripts/generate-helm.sh + cd config/manager && $(KUSTOMIZE) edit set image controller=controller:latest # reset back + +## Yaml for Kubectl +kubectl-install: manifests kustomize + mkdir -p kubectl-install + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default > kubectl-install/install-secrets-operator.yaml + + # 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 command is responsible for reading the diff --git a/k8-operator/config/manager/manager.yaml b/k8-operator/config/manager/manager.yaml index eb41eff84..eae496083 100644 --- a/k8-operator/config/manager/manager.yaml +++ b/k8-operator/config/manager/manager.yaml @@ -58,42 +58,42 @@ spec: seccompProfile: type: RuntimeDefault containers: - - command: - - /manager - args: - - --leader-elect - - --health-probe-bind-address=:8081 - image: controller:latest - name: manager - ports: [] - securityContext: - readOnlyRootFilesystem: true - allowPrivilegeEscalation: false - capabilities: - drop: - - "ALL" - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - # TODO(user): Configure the resources accordingly based on the project requirements. - # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi - volumeMounts: [] + - command: + - /manager + args: + - --leader-elect + - --health-probe-bind-address=:8081 + image: controller:latest + name: manager + ports: [] + securityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + # TODO(user): Configure the resources accordingly based on the project requirements. + # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + volumeMounts: [] volumes: [] serviceAccountName: controller-manager terminationGracePeriodSeconds: 10 diff --git a/k8-operator/scripts/generate-helm.sh b/k8-operator/scripts/generate-helm.sh index b50c31052..2b6fe642d 100755 --- a/k8-operator/scripts/generate-helm.sh +++ b/k8-operator/scripts/generate-helm.sh @@ -50,77 +50,99 @@ for crd_file in "${HELM_DIR}"/templates/*crd.yaml; do echo "Completed processing for: ${crd_file}" done -# ? NOTE: Processes only the manager-rbac.yaml file -if [ -f "${HELM_DIR}/templates/manager-rbac.yaml" ]; then - echo "Processing manager-rbac.yaml file specifically" +# ? NOTE: Processes all files ending in -rbac.yaml, except metrics-reader-rbac.yaml +for rbac_file in "${HELM_DIR}/templates"/*-rbac.yaml; do + if [ -f "$rbac_file" ]; then + if [[ "$(basename "$rbac_file")" == "metrics-reader-rbac.yaml" ]]; then + echo "Skipping metrics-reader-rbac.yaml" + continue + fi + if [[ "$(basename "$rbac_file")" == "leader-election-rbac.yaml" ]]; then + echo "Skipping infisicaldynamicsecret-admin-rbac.yaml" + continue + fi - cp "${HELM_DIR}/templates/manager-rbac.yaml" "${HELM_DIR}/templates/manager-rbac.yaml.bkp" - - # extract the rules section from the original file - rules_section=$(sed -n '/^rules:/,/^---/p' "${HELM_DIR}/templates/manager-rbac.yaml.bkp" | sed '$d') - # extract the original label lines - original_labels=$(sed -n '/^ labels:/,/^roleRef:/p' "${HELM_DIR}/templates/manager-rbac.yaml.bkp" | grep "app.kubernetes.io") - - # create a new file from scratch with exactly what we want - { - # first section: Role/ClusterRole - echo "apiVersion: rbac.authorization.k8s.io/v1" - echo "{{- if and .Values.scopedNamespace .Values.scopedRBAC }}" - echo "kind: Role" - echo "{{- else }}" - echo "kind: ClusterRole" - echo "{{- end }}" - echo "metadata:" - echo " name: {{ include \"secrets-operator.fullname\" . }}-manager-role" - echo " {{- if and .Values.scopedNamespace .Values.scopedRBAC }}" - echo " namespace: {{ .Values.scopedNamespace | quote }}" - echo " {{- end }}" - echo " labels:" - echo " {{- include \"secrets-operator.labels\" . | nindent 4 }}" - - # add the existing rules section from helm-generated file - echo "$rules_section" - - # second section: RoleBinding/ClusterRoleBinding - echo "---" - echo "apiVersion: rbac.authorization.k8s.io/v1" - echo "{{- if and .Values.scopedNamespace .Values.scopedRBAC }}" - echo "kind: RoleBinding" - echo "{{- else }}" - echo "kind: ClusterRoleBinding" - echo "{{- end }}" - echo "metadata:" - echo " name: {{ include \"secrets-operator.fullname\" . }}-manager-rolebinding" - echo " {{- if and .Values.scopedNamespace .Values.scopedRBAC }}" - echo " namespace: {{ .Values.scopedNamespace | quote }}" - echo " {{- end }}" - echo " labels:" - echo "$original_labels" - echo " {{- include \"secrets-operator.labels\" . | nindent 4 }}" - - # add the roleRef section with custom logic - echo "roleRef:" - echo " apiGroup: rbac.authorization.k8s.io" - echo " {{- if and .Values.scopedNamespace .Values.scopedRBAC }}" - echo " kind: Role" - echo " {{- else }}" - echo " kind: ClusterRole" - echo " {{- end }}" - echo " name: '{{ include \"secrets-operator.fullname\" . }}-manager-role'" - - # add the subjects section - sed -n '/^subjects:/,$ p' "${HELM_DIR}/templates/manager-rbac.yaml.bkp" - } > "${HELM_DIR}/templates/manager-rbac.yaml.new" - - mv "${HELM_DIR}/templates/manager-rbac.yaml.new" "${HELM_DIR}/templates/manager-rbac.yaml" - rm "${HELM_DIR}/templates/manager-rbac.yaml.bkp" - - echo "Completed processing for manager-rbac.yaml with both role conditions and metadata applied" -fi + filename=$(basename "$rbac_file") + base_name="${filename%-rbac.yaml}" -# ? NOTE(Daniel): Processes proxy-rbac.yaml and metrics-reader-rbac.yaml -for rbac_file in "${HELM_DIR}/templates/proxy-rbac.yaml" "${HELM_DIR}/templates/metrics-reader-rbac.yaml"; do + echo "Processing $(basename "$rbac_file") file specifically" + + cp "${rbac_file}" "${rbac_file}.bkp" + + # extract the rules section from the original file + # Extract from 'rules:' until we hit a document separator or another top-level key + + if grep -q "^---" "${rbac_file}.bkp"; then + # File has document separator, extract until --- + rules_section=$(sed -n '/^rules:/,/^---/p' "${rbac_file}.bkp" | sed '$d') + else + # Simple file, extract everything from rules to end + rules_section=$(sed -n '/^rules:/,$ p' "${rbac_file}.bkp") + fi + # extract the original label lines + original_labels=$(sed -n '/^ labels:/,/^roleRef:/p' "${HELM_DIR}/templates/${rbac_file}.bkp" | grep "app.kubernetes.io" || true) + + # create a new file from scratch with exactly what we want + { + # first section: Role/ClusterRole + echo "apiVersion: rbac.authorization.k8s.io/v1" + echo "{{- if and .Values.scopedNamespace .Values.scopedRBAC }}" + echo "kind: Role" + echo "{{- else }}" + echo "kind: ClusterRole" + echo "{{- end }}" + echo "metadata:" + echo " name: {{ include \"secrets-operator.fullname\" . }}-${base_name}-role" + echo " {{- if and .Values.scopedNamespace .Values.scopedRBAC }}" + echo " namespace: {{ .Values.scopedNamespace | quote }}" + echo " {{- end }}" + echo " labels:" + echo " {{- include \"secrets-operator.labels\" . | nindent 4 }}" + + # add the existing rules section from helm-generated file + echo "$rules_section" + + # second section: RoleBinding/ClusterRoleBinding + echo "---" + echo "apiVersion: rbac.authorization.k8s.io/v1" + echo "{{- if and .Values.scopedNamespace .Values.scopedRBAC }}" + echo "kind: RoleBinding" + echo "{{- else }}" + echo "kind: ClusterRoleBinding" + echo "{{- end }}" + echo "metadata:" + echo " name: {{ include \"secrets-operator.fullname\" . }}-${base_name}-rolebinding" + echo " {{- if and .Values.scopedNamespace .Values.scopedRBAC }}" + echo " namespace: {{ .Values.scopedNamespace | quote }}" + echo " {{- end }}" + echo " labels:" + echo "$original_labels" + echo " {{- include \"secrets-operator.labels\" . | nindent 4 }}" + + # add the roleRef section with custom logic + echo "roleRef:" + echo " apiGroup: rbac.authorization.k8s.io" + echo " {{- if and .Values.scopedNamespace .Values.scopedRBAC }}" + echo " kind: Role" + echo " {{- else }}" + echo " kind: ClusterRole" + echo " {{- end }}" + echo " name: '{{ include \"secrets-operator.fullname\" . }}-${base_name}-role'" + + # add the subjects section + sed -n '/^subjects:/,$ p' "${rbac_file}.bkp" + } > "${rbac_file}.new" + + mv "${rbac_file}.new" "${rbac_file}" + rm "${rbac_file}.bkp" + + echo "Completed processing for $(basename "$rbac_file") with both role conditions and metadata applied" + fi +done + +# ? NOTE(Daniel): Processes and metrics-reader-rbac.yaml +for rbac_file in "${HELM_DIR}/templates/metrics-reader-rbac.yaml"; do if [ -f "$rbac_file" ]; then echo "Adding scopedNamespace condition to $(basename "$rbac_file")" @@ -172,9 +194,39 @@ if [ -f "${HELM_DIR}/templates/deployment.yaml" ]; then securityContext_replaced=0 in_first_securityContext=0 first_securityContext_found=0 + containers_fixed=0 + next_line_needs_dash=0 # process the file line by line while IFS= read -r line; do + # Fix containers array syntax issue + if [[ "$line" =~ ^[[:space:]]*containers:[[:space:]]*$ ]] && [ "$containers_fixed" -eq 0 ]; then + echo "$line" >> "${HELM_DIR}/templates/deployment.yaml.new" + next_line_needs_dash=1 + containers_fixed=1 + continue + fi + + # Add dash to first container item if missing + if [ "$next_line_needs_dash" -eq 1 ]; then + # Check if line already starts with a dash (after whitespace) + if [[ "$line" =~ ^[[:space:]]*-[[:space:]] ]]; then + # Already has dash, just add the line + echo "$line" >> "${HELM_DIR}/templates/deployment.yaml.new" + elif [[ "$line" =~ ^[[:space:]]*[a-zA-Z] ]]; then + # No dash but has content, add dash before the content + # Extract indentation and content + indent=$(echo "$line" | sed 's/^\([[:space:]]*\).*/\1/') + content=$(echo "$line" | sed 's/^[[:space:]]*\(.*\)/\1/') + echo "${indent}- ${content}" >> "${HELM_DIR}/templates/deployment.yaml.new" + else + # Empty line or other, just add as-is + echo "$line" >> "${HELM_DIR}/templates/deployment.yaml.new" + fi + next_line_needs_dash=0 + continue + fi + # check if this is the first securityContext line (for kube-rbac-proxy) if [[ "$line" =~ securityContext.*Values.controllerManager.kubeRbacProxy ]] && [ "$first_securityContext_found" -eq 0 ]; then echo "$line" >> "${HELM_DIR}/templates/deployment.yaml.new" @@ -240,17 +292,6 @@ if [ -f "${HELM_DIR}/values.yaml" ]; then previous_line="" # Process the file line by line while IFS= read -r line; do - - # Check if previous line includes infisical/kubernetes-operator and this line includes tag: - if [[ "$previous_line" =~ infisical/kubernetes-operator ]] && [[ "$line" =~ ^[[:space:]]*tag: ]]; then - # Get the indentation - indent=$(echo "$line" | sed 's/\(^[[:space:]]*\).*/\1/') - # Replace with our custom tag - echo "${indent}tag: " >> "${HELM_DIR}/values.yaml.new" - continue - fi - - if [[ "$line" =~ resources: ]]; then in_resources_section=1 fi From 6100086338b2f4889e143a4464b7be051b7b7e07 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Thu, 7 Aug 2025 00:55:39 +0400 Subject: [PATCH 7/9] fixed helm --- k8-operator/config/manager/kustomization.yaml | 6 ++++ k8-operator/scripts/generate-helm.sh | 34 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/k8-operator/config/manager/kustomization.yaml b/k8-operator/config/manager/kustomization.yaml index 5c5f0b84c..ad13e96b3 100644 --- a/k8-operator/config/manager/kustomization.yaml +++ b/k8-operator/config/manager/kustomization.yaml @@ -1,2 +1,8 @@ resources: - manager.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +images: +- name: controller + newName: controller + newTag: latest diff --git a/k8-operator/scripts/generate-helm.sh b/k8-operator/scripts/generate-helm.sh index 2b6fe642d..8fd1c8d2b 100755 --- a/k8-operator/scripts/generate-helm.sh +++ b/k8-operator/scripts/generate-helm.sh @@ -196,6 +196,8 @@ if [ -f "${HELM_DIR}/templates/deployment.yaml" ]; then first_securityContext_found=0 containers_fixed=0 next_line_needs_dash=0 + imagePullSecrets_added=0 + skip_imagePullSecrets_block=0 # process the file line by line while IFS= read -r line; do @@ -267,8 +269,37 @@ if [ -f "${HELM_DIR}/templates/deployment.yaml" ]; then if [[ "$line" =~ args:.*Values.controllerManager.manager.args ]]; then continue fi + + + + # check if this is the serviceAccountName line - add imagePullSecrets after it + if [[ "$line" =~ serviceAccountName.*include.*fullname ]] && [ "$imagePullSecrets_added" -eq 0 ]; then + echo "$line" >> "${HELM_DIR}/templates/deployment.yaml.new" + # Add imagePullSecrets section + echo " {{- with .Values.imagePullSecrets }}" >> "${HELM_DIR}/templates/deployment.yaml.new" + echo " imagePullSecrets:" >> "${HELM_DIR}/templates/deployment.yaml.new" + echo " {{- toYaml . | nindent 8 }}" >> "${HELM_DIR}/templates/deployment.yaml.new" + echo " {{- end }}" >> "${HELM_DIR}/templates/deployment.yaml.new" + imagePullSecrets_added=1 + continue + fi - echo "$line" >> "${HELM_DIR}/templates/deployment.yaml.new" + # skip existing imagePullSecrets sections to avoid duplicates + if [[ "$line" =~ imagePullSecrets ]] || [[ "$line" =~ "with .Values.imagePullSecrets" ]]; then + # Skip this line and the associated template block + skip_imagePullSecrets_block=1 + continue + fi + + # skip lines that are part of an existing imagePullSecrets block + if [ "$skip_imagePullSecrets_block" -eq 1 ]; then + if [[ "$line" =~ "{{- end }}" ]]; then + skip_imagePullSecrets_block=0 + fi + continue + fi + + echo "$line" >> "${HELM_DIR}/templates/deployment.yaml.new" done < "${HELM_DIR}/templates/deployment.yaml" echo " nodeSelector: {{ toYaml .Values.controllerManager.nodeSelector | nindent 8 }}" >> "${HELM_DIR}/templates/deployment.yaml.new" @@ -363,6 +394,7 @@ if [ -f "${HELM_DIR}/values.yaml" ]; then echo "scopedNamespace: \"\"" >> "${HELM_DIR}/values.yaml.new" echo "scopedRBAC: false" >> "${HELM_DIR}/values.yaml.new" echo "installCRDs: true" >> "${HELM_DIR}/values.yaml.new" + echo "imagePullSecrets: []" >> "${HELM_DIR}/values.yaml.new" # replace the original file with the new one mv "${HELM_DIR}/values.yaml.new" "${HELM_DIR}/values.yaml" From 847c50d2d4885b157045d595c7ae7aed128c53d2 Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Fri, 8 Aug 2025 05:07:43 +0400 Subject: [PATCH 8/9] feat(k8s): upgrade to kubebuilder v4 --- helm-charts/secrets-operator/Chart.yaml | 4 +- .../templates/deployment.yaml | 6 +- .../templates/manager-rbac.yaml | 2 + helm-charts/secrets-operator/values.yaml | 2 +- k8-operator/Makefile | 10 +- k8-operator/cmd/main.go | 46 +++- k8-operator/config/default/kustomization.yaml | 226 +----------------- k8-operator/config/rbac/role.yaml | 2 + .../infisicaldynamicsecret_controller.go | 16 +- .../infisicalpushsecret_controller.go | 5 +- .../controller/infisicalsecret_controller.go | 25 +- .../controllerhelpers/controllerhelpers.go | 19 +- k8-operator/internal/controllerutil/util.go | 45 ---- .../infisicaldynamicsecret/handler.go | 46 ++-- .../infisicaldynamicsecret/reconciler.go | 55 ++--- .../services/infisicalpushsecret/handler.go | 4 + .../infisicalpushsecret/reconciler.go | 45 +--- .../services/infisicalsecret/handler.go | 28 ++- .../services/infisicalsecret/reconciler.go | 105 +++----- k8-operator/internal/util/auth.go | 93 ++++++- k8-operator/internal/util/helpers.go | 4 + k8-operator/internal/util/kubernetes.go | 74 +++++- k8-operator/scripts/generate-helm.sh | 84 ++++++- k8-operator/scripts/update-version.sh | 37 --- k8-operator/test/e2e/e2e_test.go | 10 +- 25 files changed, 459 insertions(+), 534 deletions(-) delete mode 100644 k8-operator/internal/controllerutil/util.go delete mode 100755 k8-operator/scripts/update-version.sh diff --git a/helm-charts/secrets-operator/Chart.yaml b/helm-charts/secrets-operator/Chart.yaml index 30ca8b44d..ef3541183 100644 --- a/helm-charts/secrets-operator/Chart.yaml +++ b/helm-charts/secrets-operator/Chart.yaml @@ -13,9 +13,9 @@ 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: v0.9.5 +version: v0.9.6 # 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: "v0.9.5" +appVersion: "v0.9.6" diff --git a/helm-charts/secrets-operator/templates/deployment.yaml b/helm-charts/secrets-operator/templates/deployment.yaml index ca7b16401..95cf36801 100644 --- a/helm-charts/secrets-operator/templates/deployment.yaml +++ b/helm-charts/secrets-operator/templates/deployment.yaml @@ -22,7 +22,11 @@ spec: kubectl.kubernetes.io/default-container: manager spec: containers: - - args: {{- toYaml .Values.controllerManager.manager.args | nindent 8 }} + - args: + {{- toYaml .Values.controllerManager.manager.args | nindent 8 }} + {{- if and .Values.scopedNamespace .Values.scopedRBAC }} + - --namespace={{ .Values.scopedNamespace }} + {{- end }} command: - /manager env: diff --git a/helm-charts/secrets-operator/templates/manager-rbac.yaml b/helm-charts/secrets-operator/templates/manager-rbac.yaml index 9cd79264a..27f57e1a4 100644 --- a/helm-charts/secrets-operator/templates/manager-rbac.yaml +++ b/helm-charts/secrets-operator/templates/manager-rbac.yaml @@ -48,7 +48,9 @@ rules: - apiGroups: - apps resources: + - daemonsets - deployments + - statefulsets verbs: - get - list diff --git a/helm-charts/secrets-operator/values.yaml b/helm-charts/secrets-operator/values.yaml index 896c93f60..0894aa07a 100644 --- a/helm-charts/secrets-operator/values.yaml +++ b/helm-charts/secrets-operator/values.yaml @@ -12,7 +12,7 @@ controllerManager: readOnlyRootFilesystem: true image: repository: infisical/kubernetes-operator - tag: v0.9.5 + tag: v0.9.6 resources: limits: cpu: 500m diff --git a/k8-operator/Makefile b/k8-operator/Makefile index 463b1537c..ec1690744 100644 --- a/k8-operator/Makefile +++ b/k8-operator/Makefile @@ -37,7 +37,7 @@ legacy-helm: manifests kustomize helmify helm: manifests kustomize helmify cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} - ./scripts/generate-helm.sh + ./scripts/generate-helm.sh ${VERSION} cd config/manager && $(KUSTOMIZE) edit set image controller=controller:latest # reset back ## Yaml for Kubectl @@ -88,7 +88,7 @@ test: manifests generate fmt vet setup-envtest ## Run tests. # The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. # CertManager is installed by default; skip with: # - CERT_MANAGER_INSTALL_SKIP=true -KIND_CLUSTER ?= k8-operator-test-e2e +KIND_CLUSTER ?= infisical-operator-test-e2e .PHONY: setup-test-e2e setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist @@ -157,10 +157,10 @@ PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le docker-buildx: ## Build and push docker image for the manager for cross-platform support # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross - - $(CONTAINER_TOOL) buildx create --name k8-operator-builder - $(CONTAINER_TOOL) buildx use k8-operator-builder + - $(CONTAINER_TOOL) buildx create --name infisical-operator-builder + $(CONTAINER_TOOL) buildx use infisical-operator-builder - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . - - $(CONTAINER_TOOL) buildx rm k8-operator-builder + - $(CONTAINER_TOOL) buildx rm infisical-operator-builder rm Dockerfile.cross .PHONY: build-installer diff --git a/k8-operator/cmd/main.go b/k8-operator/cmd/main.go index 1b71e0024..40142a446 100644 --- a/k8-operator/cmd/main.go +++ b/k8-operator/cmd/main.go @@ -19,6 +19,7 @@ package main import ( "crypto/tls" "flag" + "fmt" "os" "path/filepath" @@ -30,6 +31,7 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/certwatcher" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -63,7 +65,10 @@ func main() { var probeAddr string var secureMetrics bool var enableHTTP2 bool + var namespace string + var tlsOpts []func(*tls.Config) + flag.StringVar(&namespace, "namespace", "", "Watch InfisicalSecrets scoped in the provided namespace only") flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") @@ -178,7 +183,7 @@ func main() { }) } - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + managerOptions := ctrl.Options{ Scheme: scheme, Metrics: metricsServerOptions, WebhookServer: webhookServer, @@ -196,32 +201,51 @@ func main() { // if you are doing or is intended to do any operation such as perform cleanups // after the manager stops then its usage might be unsafe. // LeaderElectionReleaseOnCancel: true, - }) + } + + // Only set cache options if we're namespace-scoped + if namespace != "" { + managerOptions.Cache = cache.Options{ + Scheme: scheme, + DefaultNamespaces: map[string]cache.Config{ + namespace: {}, // whichever namespace the operator is running in + }, + } + ctrl.Log.Info(fmt.Sprintf("Watching CRDs in [namespace=%s]", namespace)) + } + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), managerOptions) if err != nil { setupLog.Error(err, "unable to start manager") os.Exit(1) } if err := (&controller.InfisicalSecretReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - BaseLogger: ctrl.Log, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + BaseLogger: ctrl.Log, + Namespace: namespace, + IsNamespaceScoped: namespace != "", }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "InfisicalSecret") os.Exit(1) } if err := (&controller.InfisicalPushSecretReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - BaseLogger: ctrl.Log, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + IsNamespaceScoped: namespace != "", + Namespace: namespace, + BaseLogger: ctrl.Log, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "InfisicalPushSecret") os.Exit(1) } if err := (&controller.InfisicalDynamicSecretReconciler{ - Client: mgr.GetClient(), - Scheme: mgr.GetScheme(), - BaseLogger: ctrl.Log, + Client: mgr.GetClient(), + Scheme: mgr.GetScheme(), + BaseLogger: ctrl.Log, + IsNamespaceScoped: namespace != "", + Namespace: namespace, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "InfisicalDynamicSecret") os.Exit(1) diff --git a/k8-operator/config/default/kustomization.yaml b/k8-operator/config/default/kustomization.yaml index 8eda77014..87497e05a 100644 --- a/k8-operator/config/default/kustomization.yaml +++ b/k8-operator/config/default/kustomization.yaml @@ -1,12 +1,12 @@ # Adds namespace to all resources. -namespace: k8-operator-system +namespace: infisical-operator-system # Value of this field is prepended to the # names of all resources, e.g. a deployment named # "wordpress" becomes "alices-wordpress". # Note that it should also match with the prefix (text before '-') of the namespace # field above. -namePrefix: k8-operator- +namePrefix: infisical-operator- # Labels to add to all resources and selectors. #labels: @@ -15,220 +15,12 @@ namePrefix: k8-operator- # someName: someValue resources: -- ../crd -- ../rbac -- ../manager -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -#- ../webhook -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. -#- ../certmanager -# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. -#- ../prometheus -# [METRICS] Expose the controller manager metrics service. -- metrics_service.yaml -# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy. -# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics. -# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will -# be able to communicate with the Webhook Server. -#- ../network-policy + - ../crd + - ../rbac + - ../manager + - metrics_service.yaml -# Uncomment the patches line if you enable Metrics patches: -# [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. -# More info: https://book.kubebuilder.io/reference/metrics -- path: manager_metrics_patch.yaml - target: - kind: Deployment - -# Uncomment the patches line if you enable Metrics and CertManager -# [METRICS-WITH-CERTS] To enable metrics protected with certManager, uncomment the following line. -# This patch will protect the metrics with certManager self-signed certs. -#- path: cert_metrics_manager_patch.yaml -# target: -# kind: Deployment - -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -#- path: manager_webhook_patch.yaml -# target: -# kind: Deployment - -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. -# Uncomment the following replacements to add the cert-manager CA injection annotations -#replacements: -# - source: # Uncomment the following block to enable certificates for metrics -# kind: Service -# version: v1 -# name: controller-manager-metrics-service -# fieldPath: metadata.name -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: metrics-certs -# fieldPaths: -# - spec.dnsNames.0 -# - spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 0 -# create: true -# - select: # Uncomment the following to set the Service name for TLS config in Prometheus ServiceMonitor -# kind: ServiceMonitor -# group: monitoring.coreos.com -# version: v1 -# name: controller-manager-metrics-monitor -# fieldPaths: -# - spec.endpoints.0.tlsConfig.serverName -# options: -# delimiter: '.' -# index: 0 -# create: true - -# - source: -# kind: Service -# version: v1 -# name: controller-manager-metrics-service -# fieldPath: metadata.namespace -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: metrics-certs -# fieldPaths: -# - spec.dnsNames.0 -# - spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 1 -# create: true -# - select: # Uncomment the following to set the Service namespace for TLS in Prometheus ServiceMonitor -# kind: ServiceMonitor -# group: monitoring.coreos.com -# version: v1 -# name: controller-manager-metrics-monitor -# fieldPaths: -# - spec.endpoints.0.tlsConfig.serverName -# options: -# delimiter: '.' -# index: 1 -# create: true - -# - source: # Uncomment the following block if you have any webhook -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.name # Name of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 0 -# create: true -# - source: -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.namespace # Namespace of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 1 -# create: true - -# - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation) -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # This name should match the one in certificate.yaml -# fieldPath: .metadata.namespace # Namespace of the certificate CR -# targets: -# - select: -# kind: ValidatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - source: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPath: .metadata.name -# targets: -# - select: -# kind: ValidatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true - -# - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting ) -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPath: .metadata.namespace # Namespace of the certificate CR -# targets: -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - source: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPath: .metadata.name -# targets: -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true - -# - source: # Uncomment the following block if you have a ConversionWebhook (--conversion) -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPath: .metadata.namespace # Namespace of the certificate CR -# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. -# +kubebuilder:scaffold:crdkustomizecainjectionns -# - source: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert -# fieldPath: .metadata.name -# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. -# +kubebuilder:scaffold:crdkustomizecainjectionname + - path: manager_metrics_patch.yaml + target: + kind: Deployment diff --git a/k8-operator/config/rbac/role.yaml b/k8-operator/config/rbac/role.yaml index 67216ba4f..97151dd0b 100644 --- a/k8-operator/config/rbac/role.yaml +++ b/k8-operator/config/rbac/role.yaml @@ -40,7 +40,9 @@ rules: - apiGroups: - apps resources: + - daemonsets - deployments + - statefulsets verbs: - get - list diff --git a/k8-operator/internal/controller/infisicaldynamicsecret_controller.go b/k8-operator/internal/controller/infisicaldynamicsecret_controller.go index adbc6b03c..f63723c7d 100644 --- a/k8-operator/internal/controller/infisicaldynamicsecret_controller.go +++ b/k8-operator/internal/controller/infisicaldynamicsecret_controller.go @@ -42,9 +42,11 @@ import ( // InfisicalDynamicSecretReconciler reconciles a InfisicalDynamicSecret object type InfisicalDynamicSecretReconciler struct { client.Client - BaseLogger logr.Logger - Scheme *runtime.Scheme - Random *rand.Rand + BaseLogger logr.Logger + Scheme *runtime.Scheme + Random *rand.Rand + Namespace string + IsNamespaceScoped bool } var infisicalDynamicSecretsResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables) @@ -106,7 +108,7 @@ func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ct } // Initialize the business logic handler - handler := infisicaldynamicsecret.NewInfisicalDynamicSecretHandler(r.Client, r.Scheme) + handler := infisicaldynamicsecret.NewInfisicalDynamicSecretHandler(r.Client, r.Scheme, r.IsNamespaceScoped) err := handler.HandleLeaseRevocation(ctx, logger, &infisicalDynamicSecretCRD, infisicalDynamicSecretsResourceVariablesMap) @@ -126,7 +128,7 @@ func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ct } // Get modified/default config - infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client) + infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client, r.IsNamespaceScoped) if err != nil { logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime)) return ctrl.Result{ @@ -135,7 +137,7 @@ func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ct } // Initialize the business logic handler - handler := infisicaldynamicsecret.NewInfisicalDynamicSecretHandler(r.Client, r.Scheme) + handler := infisicaldynamicsecret.NewInfisicalDynamicSecretHandler(r.Client, r.Scheme, r.IsNamespaceScoped) // Setup API configuration through business logic err = handler.SetupAPIConfig(infisicalDynamicSecretCRD, infisicalConfig) @@ -169,7 +171,7 @@ func (r *InfisicalDynamicSecretReconciler) Reconcile(ctx context.Context, req ct }, nil } - numDeployments, err := controllerhelpers.ReconcileDeploymentsWithManagedSecrets(ctx, r.Client, logger, infisicalDynamicSecretCRD.Spec.ManagedSecretReference) + numDeployments, err := controllerhelpers.ReconcileDeploymentsWithManagedSecrets(ctx, r.Client, logger, infisicalDynamicSecretCRD.Spec.ManagedSecretReference, r.IsNamespaceScoped) handler.SetReconcileAutoRedeploymentConditionStatus(ctx, logger, &infisicalDynamicSecretCRD, numDeployments, err) if err != nil { diff --git a/k8-operator/internal/controller/infisicalpushsecret_controller.go b/k8-operator/internal/controller/infisicalpushsecret_controller.go index dc5bfad8b..e8665b5d9 100644 --- a/k8-operator/internal/controller/infisicalpushsecret_controller.go +++ b/k8-operator/internal/controller/infisicalpushsecret_controller.go @@ -45,9 +45,10 @@ import ( // InfisicalPushSecretReconciler reconciles a InfisicalPushSecretSecret object type InfisicalPushSecretReconciler struct { client.Client - IsNamespaceScoped bool BaseLogger logr.Logger Scheme *runtime.Scheme + IsNamespaceScoped bool + Namespace string } var infisicalPushSecretResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables) @@ -158,7 +159,7 @@ func (r *InfisicalPushSecretReconciler) Reconcile(ctx context.Context, req ctrl. } // Get modified/default config - infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client) + infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client, r.IsNamespaceScoped) if err != nil { if requeueTime != 0 { logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime)) diff --git a/k8-operator/internal/controller/infisicalsecret_controller.go b/k8-operator/internal/controller/infisicalsecret_controller.go index 3cea12842..d5d9c5d39 100644 --- a/k8-operator/internal/controller/infisicalsecret_controller.go +++ b/k8-operator/internal/controller/infisicalsecret_controller.go @@ -41,8 +41,10 @@ import ( // InfisicalSecretReconciler reconciles a InfisicalSecret object type InfisicalSecretReconciler struct { client.Client - BaseLogger logr.Logger - Scheme *runtime.Scheme + BaseLogger logr.Logger + Scheme *runtime.Scheme + Namespace string + IsNamespaceScoped bool } var infisicalSecretResourceVariablesMap map[string]util.ResourceVariables = make(map[string]util.ResourceVariables) @@ -51,9 +53,16 @@ func (r *InfisicalSecretReconciler) GetLogger(req ctrl.Request) logr.Logger { return r.BaseLogger.WithValues("infisicalsecret", req.NamespacedName) } -// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets,verbs=get;list;watch;create;update;patch;delete -// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/status,verbs=get;update;patch -// +kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/finalizers,verbs=update +//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=secrets.infisical.com,resources=infisicalsecrets/finalizers,verbs=update +//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;delete +//+kubebuilder:rbac:groups="",resources=configmaps,verbs=get;list;watch;create;update;delete +//+kubebuilder:rbac:groups=apps,resources=deployments;daemonsets;statefulsets,verbs=list;watch;get;update +//+kubebuilder:rbac:groups="",resources=serviceaccounts,verbs=get;list;watch +//+kubebuilder:rbac:groups="",resources=pods,verbs=get;list +//+kubebuilder:rbac:groups="authentication.k8s.io",resources=tokenreviews,verbs=create +//+kubebuilder:rbac:groups="",resources=serviceaccounts/token,verbs=create // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -138,7 +147,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ } // Get modified/default config - infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client) + infisicalConfig, err := controllerhelpers.GetInfisicalConfigMap(ctx, r.Client, r.IsNamespaceScoped) if err != nil { logger.Error(err, fmt.Sprintf("unable to fetch infisical-config. Will requeue after [requeueTime=%v]", requeueTime)) return ctrl.Result{ @@ -147,7 +156,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ } // Initialize the business logic handler - handler := infisicalsecret.NewInfisicalSecretHandler(r.Client, r.Scheme) + handler := infisicalsecret.NewInfisicalSecretHandler(r.Client, r.Scheme, r.IsNamespaceScoped) // Setup API configuration through business logic err = handler.SetupAPIConfig(infisicalSecretCRD, infisicalConfig) @@ -177,7 +186,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ }, nil } - numDeployments, err := controllerhelpers.ReconcileDeploymentsWithMultipleManagedSecrets(ctx, r.Client, logger, managedKubeSecretReferences) + numDeployments, err := controllerhelpers.ReconcileDeploymentsWithMultipleManagedSecrets(ctx, r.Client, logger, managedKubeSecretReferences, r.IsNamespaceScoped) handler.SetInfisicalAutoRedeploymentReady(ctx, logger, &infisicalSecretCRD, numDeployments, err) if err != nil { diff --git a/k8-operator/internal/controllerhelpers/controllerhelpers.go b/k8-operator/internal/controllerhelpers/controllerhelpers.go index 0149d90a0..d33097c37 100644 --- a/k8-operator/internal/controllerhelpers/controllerhelpers.go +++ b/k8-operator/internal/controllerhelpers/controllerhelpers.go @@ -7,6 +7,7 @@ import ( "github.com/Infisical/infisical/k8-operator/api/v1alpha1" "github.com/Infisical/infisical/k8-operator/internal/constants" + "github.com/Infisical/infisical/k8-operator/internal/util" "github.com/go-logr/logr" v1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -19,7 +20,7 @@ import ( const DEPLOYMENT_SECRET_NAME_ANNOTATION_PREFIX = "secrets.infisical.com/managed-secret" const AUTO_RELOAD_DEPLOYMENT_ANNOTATION = "secrets.infisical.com/auto-reload" // needs to be set to true for a deployment to start auto redeploying -func ReconcileDeploymentsWithManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecret v1alpha1.ManagedKubeSecretConfig) (int, error) { +func ReconcileDeploymentsWithManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecret v1alpha1.ManagedKubeSecretConfig, isNamespaceScoped bool) (int, error) { listOfDeployments := &v1.DeploymentList{} err := client.List(ctx, listOfDeployments, &controllerClient.ListOptions{Namespace: managedSecret.SecretNamespace}) @@ -47,6 +48,10 @@ func ReconcileDeploymentsWithManagedSecrets(ctx context.Context, client controll managedKubeSecret := &corev1.Secret{} err = client.Get(ctx, managedKubeSecretNameAndNamespace, managedKubeSecret) if err != nil { + if util.IsNamespaceScopedError(err, isNamespaceScoped) { + return 0, fmt.Errorf("unable to fetch Kubernetes secret to update deployment. Your Operator is namespace scoped, and cannot read secrets outside of its namespace. Please ensure the secret is in the same namespace as the operator. [err=%v]", err) + } + return 0, fmt.Errorf("unable to fetch Kubernetes secret to update deployment: %v", err) } @@ -100,9 +105,9 @@ func ReconcileDeploymentsWithManagedSecrets(ctx context.Context, client controll return 0, nil } -func ReconcileDeploymentsWithMultipleManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecrets []v1alpha1.ManagedKubeSecretConfig) (int, error) { +func ReconcileDeploymentsWithMultipleManagedSecrets(ctx context.Context, client controllerClient.Client, logger logr.Logger, managedSecrets []v1alpha1.ManagedKubeSecretConfig, isNamespaceScoped bool) (int, error) { for _, managedSecret := range managedSecrets { - _, err := ReconcileDeploymentsWithManagedSecrets(ctx, client, logger, managedSecret) + _, err := ReconcileDeploymentsWithManagedSecrets(ctx, client, logger, managedSecret, isNamespaceScoped) if err != nil { logger.Error(err, fmt.Sprintf("unable to reconcile deployments with managed secret [name=%v]", managedSecret.SecretName)) return 0, err @@ -259,11 +264,17 @@ func ReconcileStatefulSet(ctx context.Context, client controllerClient.Client, l return nil } -func GetInfisicalConfigMap(ctx context.Context, client client.Client) (configMap map[string]string, errToReturn error) { +func GetInfisicalConfigMap(ctx context.Context, client client.Client, isNamespaceScoped bool) (configMap map[string]string, errToReturn error) { // default key values defaultConfigMapData := make(map[string]string) defaultConfigMapData["hostAPI"] = constants.INFISICAL_DOMAIN + // this will never work if we're namespace scoped, because the operator can't read outside of its namespace by our current RBAC rules. + // This is how it has always worked, but the error has been masked as 'not found' in V3 kubebuilder. + if isNamespaceScoped { + return defaultConfigMapData, nil + } + kubeConfigMap := &corev1.ConfigMap{} err := client.Get(ctx, types.NamespacedName{ Namespace: constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, diff --git a/k8-operator/internal/controllerutil/util.go b/k8-operator/internal/controllerutil/util.go deleted file mode 100644 index 67e0cdbe4..000000000 --- a/k8-operator/internal/controllerutil/util.go +++ /dev/null @@ -1,45 +0,0 @@ -package controllerhelpers - -import ( - "context" - "fmt" - - "github.com/Infisical/infisical/k8-operator/internal/constants" - corev1 "k8s.io/api/core/v1" - k8Errors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/client" -) - -func GetInfisicalConfigMap(ctx context.Context, client client.Client) (configMap map[string]string, errToReturn error) { - // default key values - defaultConfigMapData := make(map[string]string) - defaultConfigMapData["hostAPI"] = constants.INFISICAL_DOMAIN - - kubeConfigMap := &corev1.ConfigMap{} - err := client.Get(ctx, types.NamespacedName{ - Namespace: constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, - Name: constants.OPERATOR_SETTINGS_CONFIGMAP_NAME, - }, kubeConfigMap) - - if err != nil { - if k8Errors.IsNotFound(err) { - kubeConfigMap = nil - } else { - return nil, fmt.Errorf("GetConfigMapByNamespacedName: unable to fetch config map in [namespacedName=%s] [err=%s]", constants.OPERATOR_SETTINGS_CONFIGMAP_NAMESPACE, err) - } - } - - if kubeConfigMap == nil { - return defaultConfigMapData, nil - } else { - for key, value := range defaultConfigMapData { - _, exists := kubeConfigMap.Data[key] - if !exists { - kubeConfigMap.Data[key] = value - } - } - - return kubeConfigMap.Data, nil - } -} diff --git a/k8-operator/internal/services/infisicaldynamicsecret/handler.go b/k8-operator/internal/services/infisicaldynamicsecret/handler.go index 71ce591d5..1749cf5c0 100644 --- a/k8-operator/internal/services/infisicaldynamicsecret/handler.go +++ b/k8-operator/internal/services/infisicaldynamicsecret/handler.go @@ -19,15 +19,17 @@ import ( type InfisicalDynamicSecretHandler struct { client.Client - Scheme *runtime.Scheme - Random *rand.Rand + Scheme *runtime.Scheme + Random *rand.Rand + IsNamespaceScoped bool } -func NewInfisicalDynamicSecretHandler(client client.Client, scheme *runtime.Scheme) *InfisicalDynamicSecretHandler { +func NewInfisicalDynamicSecretHandler(client client.Client, scheme *runtime.Scheme, isNamespaceScoped bool) *InfisicalDynamicSecretHandler { return &InfisicalDynamicSecretHandler{ - Client: client, - Scheme: scheme, - Random: rand.New(rand.NewSource(time.Now().UnixNano())), + Client: client, + Scheme: scheme, + Random: rand.New(rand.NewSource(time.Now().UnixNano())), + IsNamespaceScoped: isNamespaceScoped, } } @@ -51,6 +53,10 @@ func (h *InfisicalDynamicSecretHandler) getInfisicalCaCertificateFromKubeSecret( return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err) } + if util.IsNamespaceScopedError(err, h.IsNamespaceScoped) { + return "", fmt.Errorf("unable to fetch Kubernetes CA certificate secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the CA certificate secret is in the same namespace as the operator. [err=%v]", err) + } + if err != nil { return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err) } @@ -75,36 +81,40 @@ func (h *InfisicalDynamicSecretHandler) HandleCACertificate(ctx context.Context, func (h *InfisicalDynamicSecretHandler) ReconcileInfisicalDynamicSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) (time.Duration, error) { reconciler := &InfisicalDynamicSecretReconciler{ - Client: h.Client, - Scheme: h.Scheme, - Random: h.Random, + Client: h.Client, + Scheme: h.Scheme, + Random: h.Random, + IsNamespaceScoped: h.IsNamespaceScoped, } return reconciler.ReconcileInfisicalDynamicSecret(ctx, logger, infisicalDynamicSecret, resourceVariablesMap) } func (h *InfisicalDynamicSecretHandler) HandleLeaseRevocation(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) error { reconciler := &InfisicalDynamicSecretReconciler{ - Client: h.Client, - Scheme: h.Scheme, - Random: h.Random, + Client: h.Client, + Scheme: h.Scheme, + Random: h.Random, + IsNamespaceScoped: h.IsNamespaceScoped, } return reconciler.HandleLeaseRevocation(ctx, logger, infisicalDynamicSecret, resourceVariablesMap) } func (h *InfisicalDynamicSecretHandler) SetReconcileConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, errorToConditionOn error) { reconciler := &InfisicalDynamicSecretReconciler{ - Client: h.Client, - Scheme: h.Scheme, - Random: h.Random, + Client: h.Client, + Scheme: h.Scheme, + Random: h.Random, + IsNamespaceScoped: h.IsNamespaceScoped, } reconciler.SetReconcileConditionStatus(ctx, logger, infisicalDynamicSecret, errorToConditionOn) } func (h *InfisicalDynamicSecretHandler) SetReconcileAutoRedeploymentConditionStatus(ctx context.Context, logger logr.Logger, infisicalDynamicSecret *v1alpha1.InfisicalDynamicSecret, numDeployments int, errorToConditionOn error) { reconciler := &InfisicalDynamicSecretReconciler{ - Client: h.Client, - Scheme: h.Scheme, - Random: h.Random, + Client: h.Client, + Scheme: h.Scheme, + Random: h.Random, + IsNamespaceScoped: h.IsNamespaceScoped, } reconciler.SetReconcileAutoRedeploymentConditionStatus(ctx, logger, infisicalDynamicSecret, numDeployments, errorToConditionOn) } diff --git a/k8-operator/internal/services/infisicaldynamicsecret/reconciler.go b/k8-operator/internal/services/infisicaldynamicsecret/reconciler.go index 6525dc433..e763e9308 100644 --- a/k8-operator/internal/services/infisicaldynamicsecret/reconciler.go +++ b/k8-operator/internal/services/infisicaldynamicsecret/reconciler.go @@ -2,7 +2,6 @@ package infisicaldynamicsecret import ( "context" - "errors" "fmt" "math/rand" "strings" @@ -27,8 +26,9 @@ import ( type InfisicalDynamicSecretReconciler struct { client.Client - Scheme *runtime.Scheme - Random *rand.Rand + Scheme *runtime.Scheme + Random *rand.Rand + IsNamespaceScoped bool } func (r *InfisicalDynamicSecretReconciler) createInfisicalManagedKubeSecret(ctx context.Context, logger logr.Logger, infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, versionAnnotationValue string) error { @@ -85,36 +85,6 @@ func (r *InfisicalDynamicSecretReconciler) createInfisicalManagedKubeSecret(ctx return nil } -func (r *InfisicalDynamicSecretReconciler) handleAuthentication(ctx context.Context, infisicalSecret v1alpha1.InfisicalDynamicSecret, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error) { - authStrategies := map[util.AuthStrategyType]func(ctx context.Context, reconcilerClient client.Client, secretCrd util.SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error){ - util.AuthStrategy.UNIVERSAL_MACHINE_IDENTITY: util.HandleUniversalAuth, - util.AuthStrategy.KUBERNETES_MACHINE_IDENTITY: util.HandleKubernetesAuth, - util.AuthStrategy.AWS_IAM_MACHINE_IDENTITY: util.HandleAwsIamAuth, - util.AuthStrategy.AZURE_MACHINE_IDENTITY: util.HandleAzureAuth, - util.AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: util.HandleGcpIdTokenAuth, - util.AuthStrategy.GCP_IAM_MACHINE_IDENTITY: util.HandleGcpIamAuth, - util.AuthStrategy.LDAP_MACHINE_IDENTITY: util.HandleLdapAuth, - } - - for authStrategy, authHandler := range authStrategies { - authDetails, err := authHandler(ctx, r.Client, util.SecretAuthInput{ - Secret: infisicalSecret, - Type: util.SecretCrd.INFISICAL_DYNAMIC_SECRET, - }, infisicalClient) - - if err == nil { - return authDetails, nil - } - - if !errors.Is(err, util.ErrAuthNotApplicable) { - return util.AuthenticationDetails{}, fmt.Errorf("authentication failed for strategy [%s] [err=%w]", authStrategy, err) - } - } - - return util.AuthenticationDetails{}, fmt.Errorf("no authentication method provided") - -} - func (r *InfisicalDynamicSecretReconciler) getResourceVariables(infisicalDynamicSecret v1alpha1.InfisicalDynamicSecret, resourceVariablesMap map[string]util.ResourceVariables) util.ResourceVariables { var resourceVariables util.ResourceVariables @@ -254,7 +224,10 @@ func (r *InfisicalDynamicSecretReconciler) HandleLeaseRevocation(ctx context.Con infisicalClient := resourceVariables.InfisicalClient logger.Info("Authenticating for lease revocation") - authDetails, err := r.handleAuthentication(ctx, *infisicalDynamicSecret, infisicalClient) + authDetails, err := util.HandleAuthentication(ctx, util.SecretAuthInput{ + Secret: *infisicalDynamicSecret, + Type: util.SecretCrd.INFISICAL_DYNAMIC_SECRET, + }, r.Client, infisicalClient, r.IsNamespaceScoped) if err != nil { return fmt.Errorf("unable to authenticate for lease revocation [err=%s]", err) @@ -290,6 +263,9 @@ func (r *InfisicalDynamicSecretReconciler) HandleLeaseRevocation(ctx context.Con }) if err != nil { + if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) { + return fmt.Errorf("unable to fetch Kubernetes destination secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the destination secret is in the same namespace as the operator. [err=%v]", err) + } return fmt.Errorf("unable to fetch destination secret [err=%s]", err) } @@ -318,7 +294,10 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c if authDetails.AuthStrategy == "" { logger.Info("No authentication strategy found. Attempting to authenticate") - authDetails, err = r.handleAuthentication(ctx, *infisicalDynamicSecret, infisicalClient) + authDetails, err = util.HandleAuthentication(ctx, util.SecretAuthInput{ + Secret: *infisicalDynamicSecret, + Type: util.SecretCrd.INFISICAL_DYNAMIC_SECRET, + }, r.Client, infisicalClient, r.IsNamespaceScoped) if err != nil { return nextReconcile, fmt.Errorf("unable to authenticate [err=%s]", err) @@ -337,6 +316,9 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c }) if err != nil { + if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) { + return nextReconcile, fmt.Errorf("unable to fetch Kubernetes destination secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the destination secret is in the same namespace as the operator. [err=%v]", err) + } if k8Errors.IsNotFound(err) { annotationValue := "" @@ -352,6 +334,9 @@ func (r *InfisicalDynamicSecretReconciler) ReconcileInfisicalDynamicSecret(ctx c }) if err != nil { + if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) { + return nextReconcile, fmt.Errorf("unable to fetch Kubernetes destination secret after creation. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the destination secret is in the same namespace as the operator. [err=%v]", err) + } return nextReconcile, fmt.Errorf("unable to fetch destination secret after creation [err=%s]", err) } diff --git a/k8-operator/internal/services/infisicalpushsecret/handler.go b/k8-operator/internal/services/infisicalpushsecret/handler.go index abe0266d6..476b2bda2 100644 --- a/k8-operator/internal/services/infisicalpushsecret/handler.go +++ b/k8-operator/internal/services/infisicalpushsecret/handler.go @@ -49,6 +49,10 @@ func (h *InfisicalPushSecretHandler) getInfisicalCaCertificateFromKubeSecret(ctx return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err) } + if util.IsNamespaceScopedError(err, h.IsNamespaceScoped) { + return "", fmt.Errorf("unable to fetch Kubernetes CA certificate secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the CA certificate secret is in the same namespace as the operator. [err=%v]", err) + } + if err != nil { return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err) } diff --git a/k8-operator/internal/services/infisicalpushsecret/reconciler.go b/k8-operator/internal/services/infisicalpushsecret/reconciler.go index 6c4de31c8..cdfc326bb 100644 --- a/k8-operator/internal/services/infisicalpushsecret/reconciler.go +++ b/k8-operator/internal/services/infisicalpushsecret/reconciler.go @@ -3,7 +3,6 @@ package infisicalpushsecret import ( "bytes" "context" - "errors" "fmt" "strings" tpl "text/template" @@ -30,36 +29,6 @@ type InfisicalPushSecretReconciler struct { IsNamespaceScoped bool } -func (r *InfisicalPushSecretReconciler) handleAuthentication(ctx context.Context, infisicalSecret v1alpha1.InfisicalPushSecret, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error) { - authStrategies := map[util.AuthStrategyType]func(ctx context.Context, reconcilerClient client.Client, secretCrd util.SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error){ - util.AuthStrategy.UNIVERSAL_MACHINE_IDENTITY: util.HandleUniversalAuth, - util.AuthStrategy.KUBERNETES_MACHINE_IDENTITY: util.HandleKubernetesAuth, - util.AuthStrategy.AWS_IAM_MACHINE_IDENTITY: util.HandleAwsIamAuth, - util.AuthStrategy.AZURE_MACHINE_IDENTITY: util.HandleAzureAuth, - util.AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: util.HandleGcpIdTokenAuth, - util.AuthStrategy.GCP_IAM_MACHINE_IDENTITY: util.HandleGcpIamAuth, - util.AuthStrategy.LDAP_MACHINE_IDENTITY: util.HandleLdapAuth, - } - - for authStrategy, authHandler := range authStrategies { - authDetails, err := authHandler(ctx, r.Client, util.SecretAuthInput{ - Secret: infisicalSecret, - Type: util.SecretCrd.INFISICAL_PUSH_SECRET, - }, infisicalClient) - - if err == nil { - return authDetails, nil - } - - if !errors.Is(err, util.ErrAuthNotApplicable) { - return util.AuthenticationDetails{}, fmt.Errorf("authentication failed for strategy [%s] [err=%w]", authStrategy, err) - } - } - - return util.AuthenticationDetails{}, fmt.Errorf("no authentication method provided") - -} - func (r *InfisicalPushSecretReconciler) getResourceVariables(infisicalPushSecret v1alpha1.InfisicalPushSecret, resourceVariablesMap map[string]util.ResourceVariables) util.ResourceVariables { var resourceVariables util.ResourceVariables @@ -192,7 +161,10 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context if authDetails.AuthStrategy == "" { logger.Info("No authentication strategy found. Attempting to authenticate") - authDetails, err = r.handleAuthentication(ctx, *infisicalPushSecret, infisicalClient) + authDetails, err = util.HandleAuthentication(ctx, util.SecretAuthInput{ + Secret: *infisicalPushSecret, + Type: util.SecretCrd.INFISICAL_PUSH_SECRET, + }, r.Client, infisicalClient, r.IsNamespaceScoped) r.SetAuthenticatedStatusCondition(ctx, infisicalPushSecret, err) if err != nil { @@ -215,6 +187,10 @@ func (r *InfisicalPushSecretReconciler) ReconcileInfisicalPushSecret(ctx context }) if err != nil { + if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) { + return fmt.Errorf("unable to fetch Kubernetes destination secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the destination secret is in the same namespace as the operator. [err=%v]", err) + } + return fmt.Errorf("unable to fetch kube secret [err=%s]", err) } @@ -539,7 +515,10 @@ func (r *InfisicalPushSecretReconciler) DeleteManagedSecrets(ctx context.Context if authDetails.AuthStrategy == "" { logger.Info("No authentication strategy found. Attempting to authenticate") - authDetails, err = r.handleAuthentication(ctx, *infisicalPushSecret, infisicalClient) + authDetails, err = util.HandleAuthentication(ctx, util.SecretAuthInput{ + Secret: *infisicalPushSecret, + Type: util.SecretCrd.INFISICAL_PUSH_SECRET, + }, r.Client, infisicalClient, r.IsNamespaceScoped) r.SetAuthenticatedStatusCondition(ctx, infisicalPushSecret, err) if err != nil { diff --git a/k8-operator/internal/services/infisicalsecret/handler.go b/k8-operator/internal/services/infisicalsecret/handler.go index ec3b50671..5657d701d 100644 --- a/k8-operator/internal/services/infisicalsecret/handler.go +++ b/k8-operator/internal/services/infisicalsecret/handler.go @@ -17,13 +17,15 @@ import ( type InfisicalSecretHandler struct { client.Client - Scheme *runtime.Scheme + Scheme *runtime.Scheme + IsNamespaceScoped bool } -func NewInfisicalSecretHandler(client client.Client, scheme *runtime.Scheme) *InfisicalSecretHandler { +func NewInfisicalSecretHandler(client client.Client, scheme *runtime.Scheme, isNamespaceScoped bool) *InfisicalSecretHandler { return &InfisicalSecretHandler{ - Client: client, - Scheme: scheme, + Client: client, + Scheme: scheme, + IsNamespaceScoped: isNamespaceScoped, } } @@ -48,6 +50,9 @@ func (h *InfisicalSecretHandler) getInfisicalCaCertificateFromKubeSecret(ctx con } if err != nil { + if util.IsNamespaceScopedError(err, h.IsNamespaceScoped) { + return "", fmt.Errorf("unable to fetch Kubernetes CA certificate secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the CA certificate secret is in the same namespace as the operator. [err=%v]", err) + } return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err) } @@ -71,24 +76,27 @@ func (h *InfisicalSecretHandler) HandleCACertificate(ctx context.Context, infisi func (h *InfisicalSecretHandler) ReconcileInfisicalSecret(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, managedKubeSecretReferences []v1alpha1.ManagedKubeSecretConfig, managedKubeConfigMapReferences []v1alpha1.ManagedKubeConfigMapConfig, resourceVariablesMap map[string]util.ResourceVariables) (int, error) { reconciler := &InfisicalSecretReconciler{ - Client: h.Client, - Scheme: h.Scheme, + Client: h.Client, + Scheme: h.Scheme, + IsNamespaceScoped: h.IsNamespaceScoped, } return reconciler.ReconcileInfisicalSecret(ctx, logger, infisicalSecret, managedKubeSecretReferences, managedKubeConfigMapReferences, resourceVariablesMap) } func (h *InfisicalSecretHandler) SetReadyToSyncSecretsConditions(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, secretsCount int, errorToConditionOn error) { reconciler := &InfisicalSecretReconciler{ - Client: h.Client, - Scheme: h.Scheme, + Client: h.Client, + Scheme: h.Scheme, + IsNamespaceScoped: h.IsNamespaceScoped, } reconciler.SetReadyToSyncSecretsConditions(ctx, logger, infisicalSecret, secretsCount, errorToConditionOn) } func (h *InfisicalSecretHandler) SetInfisicalAutoRedeploymentReady(ctx context.Context, logger logr.Logger, infisicalSecret *v1alpha1.InfisicalSecret, numDeployments int, errorToConditionOn error) { reconciler := &InfisicalSecretReconciler{ - Client: h.Client, - Scheme: h.Scheme, + Client: h.Client, + Scheme: h.Scheme, + IsNamespaceScoped: h.IsNamespaceScoped, } reconciler.SetInfisicalAutoRedeploymentReady(ctx, logger, infisicalSecret, numDeployments, errorToConditionOn) } diff --git a/k8-operator/internal/services/infisicalsecret/reconciler.go b/k8-operator/internal/services/infisicalsecret/reconciler.go index 1c175bae3..94596fb8c 100644 --- a/k8-operator/internal/services/infisicalsecret/reconciler.go +++ b/k8-operator/internal/services/infisicalsecret/reconciler.go @@ -32,59 +32,8 @@ const FINALIZER_NAME = "secrets.finalizers.infisical.com" type InfisicalSecretReconciler struct { client.Client - Scheme *runtime.Scheme -} - -func (r *InfisicalSecretReconciler) handleAuthentication(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error) { - - // ? Legacy support, service token auth - infisicalToken, err := r.getInfisicalTokenFromKubeSecret(ctx, infisicalSecret) - if err != nil { - return util.AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get service token from kube secret [err=%s]", err) - } - if infisicalToken != "" { - infisicalClient.Auth().SetAccessToken(infisicalToken) - return util.AuthenticationDetails{AuthStrategy: util.AuthStrategy.SERVICE_TOKEN}, nil - } - - // ? Legacy support, service account auth - serviceAccountCreds, err := r.getInfisicalServiceAccountCredentialsFromKubeSecret(ctx, infisicalSecret) - if err != nil { - return util.AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get service account creds from kube secret [err=%s]", err) - } - - if serviceAccountCreds.AccessKey != "" || serviceAccountCreds.PrivateKey != "" || serviceAccountCreds.PublicKey != "" { - infisicalClient.Auth().SetAccessToken(serviceAccountCreds.AccessKey) - return util.AuthenticationDetails{AuthStrategy: util.AuthStrategy.SERVICE_ACCOUNT}, nil - } - - authStrategies := map[util.AuthStrategyType]func(ctx context.Context, reconcilerClient client.Client, secretCrd util.SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (util.AuthenticationDetails, error){ - util.AuthStrategy.UNIVERSAL_MACHINE_IDENTITY: util.HandleUniversalAuth, - util.AuthStrategy.KUBERNETES_MACHINE_IDENTITY: util.HandleKubernetesAuth, - util.AuthStrategy.AWS_IAM_MACHINE_IDENTITY: util.HandleAwsIamAuth, - util.AuthStrategy.AZURE_MACHINE_IDENTITY: util.HandleAzureAuth, - util.AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: util.HandleGcpIdTokenAuth, - util.AuthStrategy.GCP_IAM_MACHINE_IDENTITY: util.HandleGcpIamAuth, - util.AuthStrategy.LDAP_MACHINE_IDENTITY: util.HandleLdapAuth, - } - - for authStrategy, authHandler := range authStrategies { - authDetails, err := authHandler(ctx, r.Client, util.SecretAuthInput{ - Secret: infisicalSecret, - Type: util.SecretCrd.INFISICAL_SECRET, - }, infisicalClient) - - if err == nil { - return authDetails, nil - } - - if !errors.Is(err, util.ErrAuthNotApplicable) { - return util.AuthenticationDetails{}, fmt.Errorf("authentication failed for strategy [%s] [err=%w]", authStrategy, err) - } - } - - return util.AuthenticationDetails{}, fmt.Errorf("no authentication method provided") - + Scheme *runtime.Scheme + IsNamespaceScoped bool } func (r *InfisicalSecretReconciler) getInfisicalTokenFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (string, error) { @@ -105,11 +54,14 @@ func (r *InfisicalSecretReconciler) getInfisicalTokenFromKubeSecret(ctx context. Name: secretName, }) - if k8Errors.IsNotFound(err) { + if k8Errors.IsNotFound(err) || (secretNamespace == "" && secretName == "") { return "", nil } if err != nil { + if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) { + return "", fmt.Errorf("unable to fetch Kubernetes CA certificate secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the CA certificate secret is in the same namespace as the operator. [err=%v]", err) + } return "", fmt.Errorf("failed to read Infisical token secret from secret named [%s] in namespace [%s]: with error [%w]", infisicalSecret.Spec.TokenSecretReference.SecretName, infisicalSecret.Spec.TokenSecretReference.SecretNamespace, err) } @@ -118,39 +70,26 @@ func (r *InfisicalSecretReconciler) getInfisicalTokenFromKubeSecret(ctx context. return strings.Replace(string(infisicalServiceToken), " ", "", -1), nil } -func (r *InfisicalSecretReconciler) getInfisicalCaCertificateFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (caCertificate string, err error) { - - caCertificateFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{ - Namespace: infisicalSecret.Spec.TLS.CaRef.SecretNamespace, - Name: infisicalSecret.Spec.TLS.CaRef.SecretName, - }) - - if k8Errors.IsNotFound(err) { - return "", fmt.Errorf("kubernetes secret containing custom CA certificate cannot be found. [err=%s]", err) - } - - if err != nil { - return "", fmt.Errorf("something went wrong when fetching your CA certificate [err=%s]", err) - } - - caCertificateFromSecret := string(caCertificateFromKubeSecret.Data[infisicalSecret.Spec.TLS.CaRef.SecretKey]) - - return caCertificateFromSecret, nil -} - // Fetches service account credentials from a Kubernetes secret specified in the infisicalSecret object, extracts the access key, public key, and private key from the secret, and returns them as a ServiceAccountCredentials object. // If any keys are missing or an error occurs, returns an empty object or an error object, respectively. func (r *InfisicalSecretReconciler) getInfisicalServiceAccountCredentialsFromKubeSecret(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (serviceAccountDetails model.ServiceAccountDetails, err error) { + + secretNamespace := infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretNamespace + secretName := infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretName + serviceAccountCredsFromKubeSecret, err := util.GetKubeSecretByNamespacedName(ctx, r.Client, types.NamespacedName{ - Namespace: infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretNamespace, - Name: infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretName, + Namespace: secretNamespace, + Name: secretName, }) - if k8Errors.IsNotFound(err) { + if k8Errors.IsNotFound(err) || (secretNamespace == "" && secretName == "") { return model.ServiceAccountDetails{}, nil } if err != nil { + if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) { + return model.ServiceAccountDetails{}, fmt.Errorf("unable to fetch Kubernetes service account credentials secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the service account credentials secret is in the same namespace as the operator. [err=%v]", err) + } return model.ServiceAccountDetails{}, fmt.Errorf("something went wrong when fetching your service account credentials [err=%s]", err) } @@ -503,7 +442,11 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context if authDetails.AuthStrategy == "" { logger.Info("No authentication strategy found. Attempting to authenticate") - authDetails, err = r.handleAuthentication(ctx, *infisicalSecret, infisicalClient) + authDetails, err = util.HandleAuthentication(ctx, util.SecretAuthInput{ + Secret: *infisicalSecret, + Type: util.SecretCrd.INFISICAL_SECRET, + }, r.Client, infisicalClient, r.IsNamespaceScoped) + r.SetInfisicalTokenLoadCondition(ctx, logger, infisicalSecret, authDetails.AuthStrategy, err) if err != nil { @@ -533,6 +476,9 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context }) if err != nil && !k8Errors.IsNotFound(err) { + if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) { + return 0, fmt.Errorf("unable to fetch Kubernetes secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the secret is in the same namespace as the operator. [err=%v]", err) + } return 0, fmt.Errorf("something went wrong when fetching the managed Kubernetes secret [%w]", err) } @@ -557,6 +503,9 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context }) if err != nil && !k8Errors.IsNotFound(err) { + if util.IsNamespaceScopedError(err, r.IsNamespaceScoped) { + return 0, fmt.Errorf("unable to fetch Kubernetes config map. Your Operator installation is namespace scoped, and cannot read config maps outside of the namespace it is installed in. Please ensure the config map is in the same namespace as the operator. [err=%v]", err) + } return 0, fmt.Errorf("something went wrong when fetching the managed Kubernetes config map [%w]", err) } diff --git a/k8-operator/internal/util/auth.go b/k8-operator/internal/util/auth.go index ab5802d61..81e57def3 100644 --- a/k8-operator/internal/util/auth.go +++ b/k8-operator/internal/util/auth.go @@ -16,7 +16,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) -func GetServiceAccountToken(k8sClient client.Client, namespace string, serviceAccountName string, autoCreateServiceAccountToken bool, serviceAccountTokenAudiences []string) (string, error) { +func GetServiceAccountToken(k8sClient client.Client, namespace string, serviceAccountName string, autoCreateServiceAccountToken bool, serviceAccountTokenAudiences []string, isNamespaceScoped bool) (string, error) { if autoCreateServiceAccountToken { restClient, err := GetRestClientFromClient() @@ -57,6 +57,9 @@ func GetServiceAccountToken(k8sClient client.Client, namespace string, serviceAc serviceAccount := &corev1.ServiceAccount{} err := k8sClient.Get(context.TODO(), client.ObjectKey{Name: serviceAccountName, Namespace: namespace}, serviceAccount) if err != nil { + if IsNamespaceScopedError(err, isNamespaceScoped) { + return "", fmt.Errorf("unable to fetch service account. Your Operator is namespace scoped, and cannot read secrets outside of its namespace. Please ensure the service account is in the same namespace as the operator. [err=%v]", err) + } return "", err } @@ -69,6 +72,9 @@ func GetServiceAccountToken(k8sClient client.Client, namespace string, serviceAc secret := &corev1.Secret{} err = k8sClient.Get(context.TODO(), client.ObjectKey{Name: secretName, Namespace: namespace}, secret) if err != nil { + if IsNamespaceScopedError(err, isNamespaceScoped) { + return "", fmt.Errorf("unable to fetch service account token secret. Your Operator is namespace scoped, and cannot read secrets outside of its namespace. Please ensure the service account token secret is in the same namespace as the operator. [err=%v]", err) + } return "", err } @@ -127,7 +133,7 @@ type AuthenticationDetails struct { var ErrAuthNotApplicable = errors.New("authentication not applicable") -func HandleUniversalAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { +func HandleUniversalAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, isNamespaceScoped bool) (AuthenticationDetails, error) { var universalAuthSpec v1alpha1.UniversalAuthDetails @@ -164,10 +170,14 @@ func HandleUniversalAuth(ctx context.Context, reconcilerClient client.Client, se } } + if universalAuthSpec.CredentialsRef.SecretName == "" || universalAuthSpec.CredentialsRef.SecretNamespace == "" { + return AuthenticationDetails{}, ErrAuthNotApplicable + } + universalAuthKubeSecret, err := GetInfisicalUniversalAuthFromKubeSecret(ctx, reconcilerClient, v1alpha1.KubeSecretReference{ SecretNamespace: universalAuthSpec.CredentialsRef.SecretNamespace, SecretName: universalAuthSpec.CredentialsRef.SecretName, - }) + }, isNamespaceScoped) if err != nil { return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get machine identity creds from kube secret [err=%s]", err) @@ -190,7 +200,7 @@ func HandleUniversalAuth(ctx context.Context, reconcilerClient client.Client, se }, nil } -func HandleLdapAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { +func HandleLdapAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, isNamespaceScoped bool) (AuthenticationDetails, error) { var ldapAuthSpec v1alpha1.LdapAuthDetails @@ -229,10 +239,14 @@ func HandleLdapAuth(ctx context.Context, reconcilerClient client.Client, secretC } } + if ldapAuthSpec.CredentialsRef.SecretName == "" || ldapAuthSpec.CredentialsRef.SecretNamespace == "" { + return AuthenticationDetails{}, ErrAuthNotApplicable + } + ldapAuthKubeSecret, err := GetInfisicalLdapAuthFromKubeSecret(ctx, reconcilerClient, v1alpha1.KubeSecretReference{ SecretNamespace: ldapAuthSpec.CredentialsRef.SecretNamespace, SecretName: ldapAuthSpec.CredentialsRef.SecretName, - }) + }, isNamespaceScoped) if err != nil { return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get machine identity creds from kube secret [err=%s]", err) @@ -255,7 +269,7 @@ func HandleLdapAuth(ctx context.Context, reconcilerClient client.Client, secretC }, nil } -func HandleKubernetesAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { +func HandleKubernetesAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, isNamespaceScoped bool) (AuthenticationDetails, error) { var kubernetesAuthSpec v1alpha1.KubernetesAuthDetails switch secretCrd.Type { @@ -312,6 +326,7 @@ func HandleKubernetesAuth(ctx context.Context, reconcilerClient client.Client, s kubernetesAuthSpec.ServiceAccountRef.Name, kubernetesAuthSpec.AutoCreateServiceAccountToken, kubernetesAuthSpec.ServiceAccountTokenAudiences, + isNamespaceScoped, ) if err != nil { @@ -332,7 +347,7 @@ func HandleKubernetesAuth(ctx context.Context, reconcilerClient client.Client, s } -func HandleAwsIamAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { +func HandleAwsIamAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, _ bool) (AuthenticationDetails, error) { awsIamAuthSpec := v1alpha1.AWSIamAuthDetails{} switch secretCrd.Type { @@ -387,7 +402,7 @@ func HandleAwsIamAuth(ctx context.Context, reconcilerClient client.Client, secre } -func HandleAzureAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { +func HandleAzureAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, _ bool) (AuthenticationDetails, error) { azureAuthSpec := v1alpha1.AzureAuthDetails{} switch secretCrd.Type { @@ -445,7 +460,7 @@ func HandleAzureAuth(ctx context.Context, reconcilerClient client.Client, secret } -func HandleGcpIdTokenAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { +func HandleGcpIdTokenAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, _ bool) (AuthenticationDetails, error) { gcpIdTokenSpec := v1alpha1.GCPIdTokenAuthDetails{} switch secretCrd.Type { @@ -500,7 +515,7 @@ func HandleGcpIdTokenAuth(ctx context.Context, reconcilerClient client.Client, s } -func HandleGcpIamAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface) (AuthenticationDetails, error) { +func HandleGcpIamAuth(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, _ bool) (AuthenticationDetails, error) { gcpIamSpec := v1alpha1.GcpIamAuthDetails{} switch secretCrd.Type { @@ -555,3 +570,61 @@ func HandleGcpIamAuth(ctx context.Context, reconcilerClient client.Client, secre SecretType: secretCrd.Type, }, nil } + +func HandleAuthentication(ctx context.Context, secretInput SecretAuthInput, reconcilerClient client.Client, infisicalClient infisicalSdk.InfisicalClientInterface, isNamespaceScoped bool) (AuthenticationDetails, error) { + + // We only support legacy auth for InfisicalSecret CRD + if secretInput.Type == SecretCrd.INFISICAL_SECRET { + infisicalSecret, ok := secretInput.Secret.(v1alpha1.InfisicalSecret) + + if !ok { + return AuthenticationDetails{}, errors.New("unable to cast secret to InfisicalSecret") + } + + // ? Legacy support, service token auth + infisicalToken, err := GetInfisicalTokenFromKubeSecret(ctx, reconcilerClient, infisicalSecret) + if err != nil { + return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get service token from kube secret [err=%s]", err) + } + if infisicalToken != "" { + infisicalClient.Auth().SetAccessToken(infisicalToken) + return AuthenticationDetails{AuthStrategy: AuthStrategy.SERVICE_TOKEN}, nil + } + + // ? Legacy support, service account auth + serviceAccountCreds, err := GetInfisicalServiceAccountCredentialsFromKubeSecret(ctx, reconcilerClient, infisicalSecret) + if err != nil { + return AuthenticationDetails{}, fmt.Errorf("ReconcileInfisicalSecret: unable to get service account creds from kube secret [err=%s]", err) + } + + if serviceAccountCreds.AccessKey != "" || serviceAccountCreds.PrivateKey != "" || serviceAccountCreds.PublicKey != "" { + infisicalClient.Auth().SetAccessToken(serviceAccountCreds.AccessKey) + return AuthenticationDetails{AuthStrategy: AuthStrategy.SERVICE_ACCOUNT}, nil + } + } + + authStrategies := map[AuthStrategyType]func(ctx context.Context, reconcilerClient client.Client, secretCrd SecretAuthInput, infisicalClient infisicalSdk.InfisicalClientInterface, isNamespaceScoped bool) (AuthenticationDetails, error){ + AuthStrategy.UNIVERSAL_MACHINE_IDENTITY: HandleUniversalAuth, + AuthStrategy.KUBERNETES_MACHINE_IDENTITY: HandleKubernetesAuth, + AuthStrategy.AWS_IAM_MACHINE_IDENTITY: HandleAwsIamAuth, + AuthStrategy.AZURE_MACHINE_IDENTITY: HandleAzureAuth, + AuthStrategy.GCP_ID_TOKEN_MACHINE_IDENTITY: HandleGcpIdTokenAuth, + AuthStrategy.GCP_IAM_MACHINE_IDENTITY: HandleGcpIamAuth, + AuthStrategy.LDAP_MACHINE_IDENTITY: HandleLdapAuth, + } + + for authStrategy, authHandler := range authStrategies { + authDetails, err := authHandler(ctx, reconcilerClient, secretInput, infisicalClient, isNamespaceScoped) + + if err == nil { + return authDetails, nil + } + + if !errors.Is(err, ErrAuthNotApplicable) { + return AuthenticationDetails{}, fmt.Errorf("authentication failed for strategy [%s] [err=%w]", authStrategy, err) + } + } + + return AuthenticationDetails{}, fmt.Errorf("no authentication method provided") + +} diff --git a/k8-operator/internal/util/helpers.go b/k8-operator/internal/util/helpers.go index ef3712715..c292caf3f 100644 --- a/k8-operator/internal/util/helpers.go +++ b/k8-operator/internal/util/helpers.go @@ -54,3 +54,7 @@ func AppendAPIEndpoint(address string) string { } return address + "/api" } + +func IsNamespaceScopedError(err error, isNamespaceScoped bool) bool { + return isNamespaceScoped && err != nil && strings.Contains(err.Error(), "unknown namespace for the cache") +} diff --git a/k8-operator/internal/util/kubernetes.go b/k8-operator/internal/util/kubernetes.go index 103da63eb..2f0dd0d9e 100644 --- a/k8-operator/internal/util/kubernetes.go +++ b/k8-operator/internal/util/kubernetes.go @@ -3,8 +3,10 @@ package util import ( "context" "fmt" + "strings" "github.com/Infisical/infisical/k8-operator/api/v1alpha1" + "github.com/Infisical/infisical/k8-operator/internal/constants" "github.com/Infisical/infisical/k8-operator/internal/model" corev1 "k8s.io/api/core/v1" k8Errors "k8s.io/apimachinery/pkg/api/errors" @@ -41,13 +43,11 @@ func GetKubeConfigMapByNamespacedName(ctx context.Context, reconcilerClient clie return kubeConfigMap, err } -func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, universalAuthRef v1alpha1.KubeSecretReference) (machineIdentityDetails model.UniversalAuthIdentityDetails, err error) { +func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, universalAuthRef v1alpha1.KubeSecretReference, isNamespaceScoped bool) (machineIdentityDetails model.UniversalAuthIdentityDetails, err error) { universalAuthCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{ Namespace: universalAuthRef.SecretNamespace, Name: universalAuthRef.SecretName, - // Namespace: infisicalSecret.Spec.Authentication.UniversalAuth.CredentialsRef.SecretNamespace, - // Name: infisicalSecret.Spec.Authentication.UniversalAuth.CredentialsRef.SecretName, }) if k8Errors.IsNotFound(err) { @@ -55,6 +55,9 @@ func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClie } if err != nil { + if IsNamespaceScopedError(err, isNamespaceScoped) { + return model.UniversalAuthIdentityDetails{}, fmt.Errorf("unable to fetch Kubernetes secret. Your Operator installation is namespace scoped, and cannot read secrets outside of the namespace it is installed in. Please ensure the secret is in the same namespace as the operator. [err=%v]", err) + } return model.UniversalAuthIdentityDetails{}, fmt.Errorf("something went wrong when fetching your machine identity credentials [err=%s]", err) } @@ -65,7 +68,7 @@ func GetInfisicalUniversalAuthFromKubeSecret(ctx context.Context, reconcilerClie } -func GetInfisicalLdapAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, ldapAuthRef v1alpha1.KubeSecretReference) (machineIdentityDetails model.LdapIdentityDetails, err error) { +func GetInfisicalLdapAuthFromKubeSecret(ctx context.Context, reconcilerClient client.Client, ldapAuthRef v1alpha1.KubeSecretReference, isNamespaceScoped bool) (machineIdentityDetails model.LdapIdentityDetails, err error) { ldapAuthCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{ Namespace: ldapAuthRef.SecretNamespace, @@ -77,6 +80,9 @@ func GetInfisicalLdapAuthFromKubeSecret(ctx context.Context, reconcilerClient cl } if err != nil { + if IsNamespaceScopedError(err, isNamespaceScoped) { + return model.LdapIdentityDetails{}, fmt.Errorf("unable to fetch Kubernetes secret. Your Operator is namespace scoped, and cannot read secrets outside of its namespace. Please ensure the secret is in the same namespace as the operator. [err=%v]", err) + } return model.LdapIdentityDetails{}, fmt.Errorf("something went wrong when fetching your machine identity credentials [err=%s]", err) } @@ -115,3 +121,63 @@ func GetRestClientFromClient() (rest.Interface, error) { return clientset.CoreV1().RESTClient(), nil } + +func GetInfisicalTokenFromKubeSecret(ctx context.Context, reconcilerClient client.Client, infisicalSecret v1alpha1.InfisicalSecret) (string, error) { + // default to new secret ref structure + secretName := infisicalSecret.Spec.Authentication.ServiceToken.ServiceTokenSecretReference.SecretName + secretNamespace := infisicalSecret.Spec.Authentication.ServiceToken.ServiceTokenSecretReference.SecretNamespace + // fall back to previous secret ref + if secretName == "" { + secretName = infisicalSecret.Spec.TokenSecretReference.SecretName + } + + if secretNamespace == "" { + secretNamespace = infisicalSecret.Spec.TokenSecretReference.SecretNamespace + } + + tokenSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{ + Namespace: secretNamespace, + Name: secretName, + }) + + if k8Errors.IsNotFound(err) || (secretNamespace == "" && secretName == "") { + return "", nil + } + + if err != nil { + return "", fmt.Errorf("failed to read Infisical token secret from secret named [%s] in namespace [%s]: with error [%w]", infisicalSecret.Spec.TokenSecretReference.SecretName, infisicalSecret.Spec.TokenSecretReference.SecretNamespace, err) + } + + infisicalServiceToken := tokenSecret.Data[constants.INFISICAL_TOKEN_SECRET_KEY_NAME] + + return strings.Replace(string(infisicalServiceToken), " ", "", -1), nil +} + +func GetInfisicalServiceAccountCredentialsFromKubeSecret(ctx context.Context, reconcilerClient client.Client, infisicalSecret v1alpha1.InfisicalSecret) (serviceAccountDetails model.ServiceAccountDetails, err error) { + + secretNamespace := infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretNamespace + secretName := infisicalSecret.Spec.Authentication.ServiceAccount.ServiceAccountSecretReference.SecretName + + serviceAccountCredsFromKubeSecret, err := GetKubeSecretByNamespacedName(ctx, reconcilerClient, types.NamespacedName{ + Namespace: secretNamespace, + Name: secretName, + }) + + if k8Errors.IsNotFound(err) || (secretNamespace == "" && secretName == "") { + return model.ServiceAccountDetails{}, nil + } + + if err != nil { + return model.ServiceAccountDetails{}, fmt.Errorf("something went wrong when fetching your service account credentials [err=%s]", err) + } + + accessKeyFromSecret := serviceAccountCredsFromKubeSecret.Data[constants.SERVICE_ACCOUNT_ACCESS_KEY] + publicKeyFromSecret := serviceAccountCredsFromKubeSecret.Data[constants.SERVICE_ACCOUNT_PUBLIC_KEY] + privateKeyFromSecret := serviceAccountCredsFromKubeSecret.Data[constants.SERVICE_ACCOUNT_PRIVATE_KEY] + + if accessKeyFromSecret == nil || publicKeyFromSecret == nil || privateKeyFromSecret == nil { + return model.ServiceAccountDetails{}, nil + } + + return model.ServiceAccountDetails{AccessKey: string(accessKeyFromSecret), PrivateKey: string(privateKeyFromSecret), PublicKey: string(publicKeyFromSecret)}, nil +} diff --git a/k8-operator/scripts/generate-helm.sh b/k8-operator/scripts/generate-helm.sh index 8fd1c8d2b..87d6ec1fb 100755 --- a/k8-operator/scripts/generate-helm.sh +++ b/k8-operator/scripts/generate-helm.sh @@ -1,12 +1,36 @@ #!/usr/bin/env bash set -euo pipefail SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd) +PATH_TO_HELM_CHART="${SCRIPT_DIR}/../../helm-charts/secrets-operator" + PROJECT_ROOT=$(cd "${SCRIPT_DIR}/.." && pwd) HELM_DIR="${PROJECT_ROOT}/../helm-charts/secrets-operator" LOCALBIN="${PROJECT_ROOT}/bin" KUSTOMIZE="${LOCALBIN}/kustomize" HELMIFY="${LOCALBIN}/helmify" +VERSION=$1 +VERSION_WITHOUT_V=$(echo "$VERSION" | sed 's/^v//') # needed to validate semver + + +# Version validation +if [ -z "$VERSION" ]; then + echo "Usage: $0 " + exit 1 +fi + + +if ! [[ "$VERSION_WITHOUT_V" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Version must follow semantic versioning (e.g. 0.0.1)" + exit 1 +fi + +if ! [[ "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Version must start with 'v' (e.g. v0.0.1)" + exit 1 +fi + + cd "${PROJECT_ROOT}" # first run the regular helm target to generate base templates @@ -309,6 +333,48 @@ if [ -f "${HELM_DIR}/templates/deployment.yaml" ]; then echo "Completed processing for deployment.yaml" fi +# ? NOTE(Daniel): Fix args structure in deployment.yaml +if [ -f "${HELM_DIR}/templates/deployment.yaml" ]; then + echo "Fixing args structure in deployment.yaml" + + touch "${HELM_DIR}/templates/deployment.yaml.tmp" + + # process the file line by line + while IFS= read -r line; do + # look for the specific line pattern: "- args: {{- toYaml .Values.controllerManager.manager.args | nindent 8 }}" + if [[ "$line" =~ ^[[:space:]]*-[[:space:]]*args:[[:space:]]*\{\{-.*toYaml.*Values\.controllerManager\.manager\.args.*\}\}[[:space:]]*$ ]]; then + # extract the base indentation (everything before the "- args:") + base_indent=$(echo "$line" | sed 's/^\([[:space:]]*\)-.*/\1/') + + # replace with our multi-line structure + echo "${base_indent}- args:" >> "${HELM_DIR}/templates/deployment.yaml.tmp" + echo "${base_indent} {{- toYaml .Values.controllerManager.manager.args | nindent 8 }}" >> "${HELM_DIR}/templates/deployment.yaml.tmp" + echo "${base_indent} {{- if and .Values.scopedNamespace .Values.scopedRBAC }}" >> "${HELM_DIR}/templates/deployment.yaml.tmp" + echo "${base_indent} - --namespace={{ .Values.scopedNamespace }}" >> "${HELM_DIR}/templates/deployment.yaml.tmp" + echo "${base_indent} {{- end }}" >> "${HELM_DIR}/templates/deployment.yaml.tmp" + else + echo "$line" >> "${HELM_DIR}/templates/deployment.yaml.tmp" + fi + done < "${HELM_DIR}/templates/deployment.yaml" + + mv "${HELM_DIR}/templates/deployment.yaml.tmp" "${HELM_DIR}/templates/deployment.yaml" + echo "Completed args structure fix" +fi + + + + + + + + + + + + + + + # ? NOTE(Daniel): Processes values.yaml if [ -f "${HELM_DIR}/values.yaml" ]; then echo "Processing values.yaml file" @@ -402,4 +468,20 @@ if [ -f "${HELM_DIR}/values.yaml" ]; then echo "Completed processing for values.yaml" fi -echo "Helm chart generation complete with custom templating applied." \ No newline at end of file +echo "Helm chart generation complete with custom templating applied." + + + + +# For Linux vs macOS sed compatibility +if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS version + sed -i '' 's/appVersion: .*/appVersion: "'"$VERSION"'"/g' "${PATH_TO_HELM_CHART}/Chart.yaml" + sed -i '' 's/version: .*/version: '"$VERSION"'/g' "${PATH_TO_HELM_CHART}/Chart.yaml" +else + # Linux version + sed -i 's/appVersion: .*/appVersion: "'"$VERSION"'"/g' "${PATH_TO_HELM_CHART}/Chart.yaml" + sed -i 's/version: .*/version: '"$VERSION"'/g' "${PATH_TO_HELM_CHART}/Chart.yaml" +fi + +echo "Helm chart version updated to ${VERSION}" \ No newline at end of file diff --git a/k8-operator/scripts/update-version.sh b/k8-operator/scripts/update-version.sh deleted file mode 100755 index e75fae10d..000000000 --- a/k8-operator/scripts/update-version.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash - -SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &> /dev/null && pwd) -PATH_TO_HELM_CHART="${SCRIPT_DIR}/../../helm-charts/secrets-operator" - -VERSION=$1 -VERSION_WITHOUT_V=$(echo "$VERSION" | sed 's/^v//') # needed to validate semver - - -if [ -z "$VERSION" ]; then - echo "Usage: $0 " - exit 1 -fi - - -if ! [[ "$VERSION_WITHOUT_V" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Error: Version must follow semantic versioning (e.g. 0.0.1)" - exit 1 -fi - -if ! [[ "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Error: Version must start with 'v' (e.g. v0.0.1)" - exit 1 -fi - -# For Linux vs macOS sed compatibility -if [[ "$OSTYPE" == "darwin"* ]]; then - # macOS version - sed -i '' -e '/repository: infisical\/kubernetes-operator/{n;s/tag: .*/tag: '"$VERSION"'/;}' "${PATH_TO_HELM_CHART}/values.yaml" - sed -i '' 's/appVersion: .*/appVersion: "'"$VERSION"'"/g' "${PATH_TO_HELM_CHART}/Chart.yaml" - sed -i '' 's/version: .*/version: '"$VERSION"'/g' "${PATH_TO_HELM_CHART}/Chart.yaml" -else - # Linux version - sed -i -e '/repository: infisical\/kubernetes-operator/{n;s/tag: .*/tag: '"$VERSION"'/;}' "${PATH_TO_HELM_CHART}/values.yaml" - sed -i 's/appVersion: .*/appVersion: "'"$VERSION"'"/g' "${PATH_TO_HELM_CHART}/Chart.yaml" - sed -i 's/version: .*/version: '"$VERSION"'/g' "${PATH_TO_HELM_CHART}/Chart.yaml" -fi \ No newline at end of file diff --git a/k8-operator/test/e2e/e2e_test.go b/k8-operator/test/e2e/e2e_test.go index 15b26328c..5f3830e45 100644 --- a/k8-operator/test/e2e/e2e_test.go +++ b/k8-operator/test/e2e/e2e_test.go @@ -31,16 +31,16 @@ import ( ) // namespace where the project is deployed in -const namespace = "k8-operator-system" +const namespace = "infisical-operator-system" // serviceAccountName created for the project -const serviceAccountName = "k8-operator-controller-manager" +const serviceAccountName = "infisical-operator-controller-manager" // metricsServiceName is the name of the metrics service of the project -const metricsServiceName = "k8-operator-controller-manager-metrics-service" +const metricsServiceName = "infisical-operator-controller-manager-metrics-service" // metricsRoleBindingName is the name of the RBAC that will be created to allow get the metrics data -const metricsRoleBindingName = "k8-operator-metrics-binding" +const metricsRoleBindingName = "infisical-operator-metrics-binding" var _ = Describe("Manager", Ordered, func() { var controllerPodName string @@ -173,7 +173,7 @@ var _ = Describe("Manager", Ordered, func() { It("should ensure the metrics endpoint is serving metrics", func() { By("creating a ClusterRoleBinding for the service account to allow access to metrics") cmd := exec.Command("kubectl", "create", "clusterrolebinding", metricsRoleBindingName, - "--clusterrole=k8-operator-metrics-reader", + "--clusterrole=infisical-operator-metrics-reader", fmt.Sprintf("--serviceaccount=%s:%s", namespace, serviceAccountName), ) _, err := utils.Run(cmd) From 22abb78f48ff19f43f1174c8f4d7040ab78b204c Mon Sep 17 00:00:00 2001 From: Daniel Hougaard Date: Fri, 8 Aug 2025 22:46:43 +0400 Subject: [PATCH 9/9] downgrade helm to fix tests --- helm-charts/secrets-operator/Chart.yaml | 4 ++-- helm-charts/secrets-operator/values.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/helm-charts/secrets-operator/Chart.yaml b/helm-charts/secrets-operator/Chart.yaml index ef3541183..30ca8b44d 100644 --- a/helm-charts/secrets-operator/Chart.yaml +++ b/helm-charts/secrets-operator/Chart.yaml @@ -13,9 +13,9 @@ 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: v0.9.6 +version: v0.9.5 # 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: "v0.9.6" +appVersion: "v0.9.5" diff --git a/helm-charts/secrets-operator/values.yaml b/helm-charts/secrets-operator/values.yaml index 0894aa07a..896c93f60 100644 --- a/helm-charts/secrets-operator/values.yaml +++ b/helm-charts/secrets-operator/values.yaml @@ -12,7 +12,7 @@ controllerManager: readOnlyRootFilesystem: true image: repository: infisical/kubernetes-operator - tag: v0.9.6 + tag: v0.9.5 resources: limits: cpu: 500m