├── .chglog ├── CHANGELOG.tpl.md └── config.yml ├── .dockerignore ├── .editorconfig ├── .github ├── CODEOWNERS ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── ci.yaml │ ├── release.yaml │ └── reviewdog.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── .tool-versions ├── Dockerfile ├── LICENSE ├── Makefile ├── PROJECT ├── README.md ├── api ├── v1alpha1 │ ├── groupversion_info.go │ ├── sopssecret_types.go │ └── zz_generated.deepcopy.go ├── v1alpha2 │ ├── groupversion_info.go │ ├── sopssecret_types.go │ └── zz_generated.deepcopy.go └── v1alpha3 │ ├── groupversion_info.go │ ├── sopssecret_types.go │ └── zz_generated.deepcopy.go ├── chart ├── crd ├── helm3 │ ├── README.md.gotmpl │ └── sops-secrets-operator │ │ ├── .helmignore │ │ ├── Chart.yaml │ │ ├── Makefile │ │ ├── README.md │ │ ├── crds │ │ └── isindir.github.com_sopssecrets.yaml │ │ ├── templates │ │ ├── NOTES.txt │ │ ├── _helpers.tpl │ │ ├── azure_secret.yaml │ │ ├── cluster_role.yaml │ │ ├── cluster_role_binding.yaml │ │ ├── gcp_secret.yaml │ │ ├── monitor.yaml │ │ ├── operator.yaml │ │ ├── service_account.yaml │ │ └── validation.yaml │ │ ├── tests │ │ ├── monitor_test.yaml │ │ ├── operator_test.yaml │ │ ├── service_account_test.yaml │ │ └── validation_test.yaml │ │ └── values.yaml └── samples ├── cmd └── main.go ├── config ├── age-test-key │ ├── 00-raw-test-secrets.yaml │ ├── 00-test-secrets.yaml │ ├── 01-raw-test-secrets.yaml │ ├── 01-test-secrets.yaml │ ├── 02-raw-test-secrets.yaml │ ├── 02-test-secrets.yaml │ └── key-file.txt ├── crd │ ├── bases │ │ └── isindir.github.com_sopssecrets.yaml │ ├── kustomization.yaml │ ├── kustomizeconfig.yaml │ └── patches │ │ ├── cainjection_in_sopssecrets.yaml │ │ └── webhook_in_sopssecrets.yaml ├── default │ ├── kustomization.yaml │ ├── manager_auth_proxy_patch.yaml │ └── manager_config_patch.yaml ├── manager │ ├── controller_manager_config.yaml │ ├── kustomization.yaml │ └── manager.yaml ├── prometheus │ ├── kustomization.yaml │ └── monitor.yaml ├── rbac │ ├── auth_proxy_client_clusterrole.yaml │ ├── auth_proxy_role.yaml │ ├── auth_proxy_role_binding.yaml │ ├── auth_proxy_service.yaml │ ├── kustomization.yaml │ ├── leader_election_role.yaml │ ├── leader_election_role_binding.yaml │ ├── role.yaml │ ├── role_binding.yaml │ ├── service_account.yaml │ ├── sopssecret_editor_role.yaml │ └── sopssecret_viewer_role.yaml └── samples │ ├── isindir_v1alpha1_sopssecret.yaml │ ├── isindir_v1alpha2_sopssecret.yaml │ └── isindir_v1alpha3_sopssecret.yaml ├── docs ├── age │ └── README.md ├── api_upgrade_example │ ├── README.md │ └── examples │ │ ├── crd.0.0.9.patched.yaml │ │ ├── crd.0.0.9.yaml │ │ ├── crd.0.3.0.patched.yaml │ │ ├── crd.0.3.0.yaml │ │ ├── plain.v1alpha1.yaml │ │ ├── plain.v1alpha3.yaml │ │ ├── values.0.0.9.custom.yaml │ │ └── values.0.3.0.custom.yaml ├── artifacthub-repo.yml ├── dev-notes │ └── Versioning-README.md ├── gpg │ ├── Makefile │ ├── README.md │ ├── generate-secrets.sh │ ├── install.sh │ └── keys-env ├── index.yaml ├── sops-secrets-operator-0.1.10.tgz ├── sops-secrets-operator-0.10.0.tgz ├── sops-secrets-operator-0.10.1.tgz ├── sops-secrets-operator-0.10.2.tgz ├── sops-secrets-operator-0.10.3.tgz ├── sops-secrets-operator-0.10.4.tgz ├── sops-secrets-operator-0.10.5.tgz ├── sops-secrets-operator-0.10.6.tgz ├── sops-secrets-operator-0.10.7.tgz ├── sops-secrets-operator-0.10.8.tgz ├── sops-secrets-operator-0.11.0.tgz ├── sops-secrets-operator-0.11.1.tgz ├── sops-secrets-operator-0.11.2.tgz ├── sops-secrets-operator-0.11.3.tgz ├── sops-secrets-operator-0.12.0.tgz ├── sops-secrets-operator-0.12.1.tgz ├── sops-secrets-operator-0.12.2.tgz ├── sops-secrets-operator-0.12.3.tgz ├── sops-secrets-operator-0.12.4.tgz ├── sops-secrets-operator-0.12.5.tgz ├── sops-secrets-operator-0.13.0.tgz ├── sops-secrets-operator-0.13.1.tgz ├── sops-secrets-operator-0.13.2.tgz ├── sops-secrets-operator-0.14.0.tgz ├── sops-secrets-operator-0.14.1.tgz ├── sops-secrets-operator-0.14.2.tgz ├── sops-secrets-operator-0.15.0.tgz ├── sops-secrets-operator-0.15.1.tgz ├── sops-secrets-operator-0.15.2.tgz ├── sops-secrets-operator-0.15.3.tgz ├── sops-secrets-operator-0.15.4.tgz ├── sops-secrets-operator-0.15.5.tgz ├── sops-secrets-operator-0.16.0.tgz ├── sops-secrets-operator-0.17.0.tgz ├── sops-secrets-operator-0.17.1.tgz ├── sops-secrets-operator-0.17.2.tgz ├── sops-secrets-operator-0.17.4.tgz ├── sops-secrets-operator-0.18.0.tgz ├── sops-secrets-operator-0.18.1.tgz ├── sops-secrets-operator-0.18.2.tgz ├── sops-secrets-operator-0.18.3.tgz ├── sops-secrets-operator-0.18.4.tgz ├── sops-secrets-operator-0.18.5.tgz ├── sops-secrets-operator-0.18.6.tgz ├── sops-secrets-operator-0.19.0.tgz ├── sops-secrets-operator-0.19.1.tgz ├── sops-secrets-operator-0.19.2.tgz ├── sops-secrets-operator-0.19.3.tgz ├── sops-secrets-operator-0.19.4.tgz ├── sops-secrets-operator-0.2.1.tgz ├── sops-secrets-operator-0.20.0.tgz ├── sops-secrets-operator-0.20.1.tgz ├── sops-secrets-operator-0.20.2.tgz ├── sops-secrets-operator-0.20.3.tgz ├── sops-secrets-operator-0.20.4.tgz ├── sops-secrets-operator-0.20.5.tgz ├── sops-secrets-operator-0.21.0.tgz ├── sops-secrets-operator-0.22.0.tgz ├── sops-secrets-operator-0.3.0.tgz ├── sops-secrets-operator-0.3.1.tgz ├── sops-secrets-operator-0.3.2.tgz ├── sops-secrets-operator-0.3.3.tgz ├── sops-secrets-operator-0.3.4.tgz ├── sops-secrets-operator-0.3.5.tgz ├── sops-secrets-operator-0.3.6.tgz ├── sops-secrets-operator-0.3.7.tgz ├── sops-secrets-operator-0.4.0.tgz ├── sops-secrets-operator-0.4.1.tgz ├── sops-secrets-operator-0.4.2.tgz ├── sops-secrets-operator-0.4.3.tgz ├── sops-secrets-operator-0.4.4.tgz ├── sops-secrets-operator-0.4.5.tgz ├── sops-secrets-operator-0.4.6.tgz ├── sops-secrets-operator-0.4.7.tgz ├── sops-secrets-operator-0.4.8.tgz ├── sops-secrets-operator-0.5.0.tgz ├── sops-secrets-operator-0.5.1.tgz ├── sops-secrets-operator-0.5.2.tgz ├── sops-secrets-operator-0.5.3.tgz ├── sops-secrets-operator-0.6.0.tgz ├── sops-secrets-operator-0.6.1.tgz ├── sops-secrets-operator-0.6.2.tgz ├── sops-secrets-operator-0.6.3.tgz ├── sops-secrets-operator-0.6.4.tgz ├── sops-secrets-operator-0.6.5.tgz ├── sops-secrets-operator-0.6.6.tgz ├── sops-secrets-operator-0.6.7.tgz ├── sops-secrets-operator-0.6.8.tgz ├── sops-secrets-operator-0.7.0.tgz ├── sops-secrets-operator-0.7.1.tgz ├── sops-secrets-operator-0.7.2.tgz ├── sops-secrets-operator-0.7.3.tgz ├── sops-secrets-operator-0.7.4.tgz ├── sops-secrets-operator-0.7.5.tgz ├── sops-secrets-operator-0.7.6.tgz ├── sops-secrets-operator-0.8.0.tgz ├── sops-secrets-operator-0.8.1.tgz ├── sops-secrets-operator-0.8.2.tgz ├── sops-secrets-operator-0.8.3.tgz ├── sops-secrets-operator-0.8.4.tgz ├── sops-secrets-operator-0.9.0.tgz ├── sops-secrets-operator-0.9.1.tgz ├── sops-secrets-operator-0.9.2.tgz ├── sops-secrets-operator-0.9.3.tgz ├── sops-secrets-operator-0.9.4.tgz ├── sops-secrets-operator-0.9.5.tgz ├── sops-secrets-operator-0.9.6.tgz └── sops-secrets-operator-0.9.7.tgz ├── go.mod ├── go.sum ├── hack └── boilerplate.go.txt └── internal └── controllers ├── custom_metrics.go ├── custom_predicates.go ├── sopssecret_controller.go ├── sopssecret_controller_test.go └── suite_test.go /.chglog/CHANGELOG.tpl.md: -------------------------------------------------------------------------------- 1 | {{ range .Versions }} 2 | {{ .Tag.Name }} 3 | 4 | > {{ datetime "2006-01-02" .Tag.Date }} 5 | 6 | ### {{ if .Tag.Previous }}Changes since {{ .Tag.Previous.Name }} for [{{ .Tag.Name }}]({{ $.Info.RepositoryURL }}/compare/{{ .Tag.Previous.Name }}...{{ .Tag.Name }}){{ else }}{{ .Tag.Name }}{{ end }} 7 | 8 | {{ range .CommitGroups -}} 9 | ### {{ .Title }} 10 | 11 | {{ range .Commits -}} 12 | * {{ .Subject }} 13 | {{ end }} 14 | {{ end -}} 15 | 16 | {{- if .RevertCommits -}} 17 | ### Reverts 18 | 19 | {{ range .RevertCommits -}} 20 | * {{ .Revert.Header }} 21 | {{ end }} 22 | {{ end -}} 23 | 24 | {{- if .MergeCommits -}} 25 | ### Pull Requests 26 | 27 | {{ range .MergeCommits -}} 28 | * {{ .Header }} 29 | {{ end }} 30 | {{ end -}} 31 | 32 | {{- if .NoteGroups -}} 33 | {{ range .NoteGroups -}} 34 | ### {{ .Title }} 35 | 36 | {{ range .Notes }} 37 | {{ .Body }} 38 | {{ end }} 39 | {{ end -}} 40 | {{ end -}} 41 | {{ end -}} 42 | -------------------------------------------------------------------------------- /.chglog/config.yml: -------------------------------------------------------------------------------- 1 | style: github 2 | template: CHANGELOG.tpl.md 3 | info: 4 | title: CHANGELOG 5 | repository_url: https://github.com/isindir/sops-secrets-operator 6 | options: 7 | commits: 8 | filters: 9 | Type: 10 | - build 11 | - ci 12 | - docs 13 | - feat 14 | - fix 15 | - perf 16 | - refactor 17 | - style 18 | - test 19 | commit_groups: 20 | title_maps: 21 | build: Build system and external dependency changes 22 | ci: CI configuration changes 23 | docs: Documentation changes 24 | feat: New features 25 | fix: Bug fixes 26 | perf: Performance improvements 27 | refactor: Code refactoring 28 | style: Cosmetic changes 29 | test: Changes to the tests 30 | header: 31 | pattern: "^(\\w*)\\:\\s(.*)$" 32 | pattern_maps: 33 | - Type 34 | - Subject 35 | notes: 36 | keywords: 37 | - BREAKING CHANGE 38 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file 2 | # Ignore all files which are not go type 3 | !**/*.go 4 | !**/*.mod 5 | !**/*.sum 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | ; Unix-style newlines with a newline ending every file 4 | [*] 5 | end_of_line = lf 6 | insert_final_newline = true 7 | trim_trailing_whitespace = true 8 | indent_style = tab 9 | indent_size = 4 10 | max_line_length = 120 11 | 12 | ; Makefiles 13 | [*Makefile*] 14 | indent_size = 2 15 | 16 | ; Python 17 | [*.py] 18 | indent_style = space 19 | 20 | ; Hy Language 21 | [*.hy] 22 | indent_style = space 23 | indent_size = 2 24 | 25 | [*.{scala,rb,java,groovy}] 26 | indent_style = space 27 | indent_size = 2 28 | 29 | ; GoLang 30 | [*.go] 31 | indent_style = tab 32 | max_line_length = 120 33 | 34 | ; YAML 35 | [*.{yml,yaml}] 36 | indent_style = space 37 | indent_size = 2 38 | 39 | ; terraform 40 | [*.{tf,tfvars}] 41 | max_line_length = 0 42 | indent_size = 2 43 | 44 | ; Javascript, Typescript, json 45 | [*.{ts,js,json}] 46 | indent_style = space 47 | indent_size = 2 48 | 49 | ; TOML 50 | [*.{toml}] 51 | indent_style = space 52 | indent_size = 2 53 | 54 | ; Markdown 55 | [*.{md,markdown}] 56 | max_line_length = 0 57 | indent_style = space 58 | indent_size = 2 59 | trim_trailing_whitespace = false 60 | 61 | ; HTML 62 | [*.{html,css,scss,sass,less}] 63 | indent_style = space 64 | indent_size = 2 65 | 66 | ; shell 67 | [*.{sh,bash,zsh,ksh}] 68 | indent_style = space 69 | 70 | ; Dockerfile 71 | [*Dockerfile*] 72 | indent_style = space 73 | indent_size = 2 74 | 75 | ; Jenkins Grovy 76 | [*Jenkinsfile*] 77 | max_line_length = 0 78 | indent_style = space 79 | indent_size = 2 80 | 81 | [COMMIT_EDITMSG] 82 | max_line_length = 0 83 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Global owners 2 | * @isindir 3 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### What this PR does / why we need it: 2 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: ci-development 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - '**' 7 | - '!master' 8 | 9 | jobs: 10 | build: 11 | 12 | name: CI 13 | environment: ci-cd 14 | # UPDATE_HERE 15 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on 16 | runs-on: ubuntu-24.04 17 | env: 18 | # UPDATE_HERE 19 | # https://hub.docker.com/r/rancher/k3s/tags 20 | K3S_VERSION: v1.33.0-k3s1 21 | # https://github.com/helm-unittest/helm-unittest/releases 22 | HELM_UNITTEST_VERSION: 0.8.2 23 | 24 | steps: 25 | 26 | # UPDATE_HERE 27 | # https://github.com/actions/checkout/releases 28 | - uses: actions/checkout@v4 29 | name: Check out code 30 | 31 | # UPDATE_HERE 32 | # https://github.com/asdf-vm/actions/releases 33 | # https://github.com/asdf-vm/actions/issues/594 34 | - name: Install asdf tools 35 | # uses: asdf-vm/actions/install@v3 36 | uses: asdf-vm/actions/install@9cd779f40fe38688dd19505ccbc4eaaf018b44e7 37 | with: 38 | asdf_version: 0.16.7 39 | 40 | - name: install gpg2 41 | run: | 42 | sudo apt update 43 | sudo apt install -f 44 | sudo apt-get install gnupg2 -y 45 | sudo apt-get install qemu-user-static -y 46 | 47 | - name: install helm unittest plugin 48 | run: | 49 | helm plugin install https://github.com/helm-unittest/helm-unittest --version ${HELM_UNITTEST_VERSION} 50 | helm unittest --help 51 | 52 | - name: Test helm charts 53 | run: make test-helm 54 | 55 | - name: Unit tests and envtest integration tests 56 | run: make test 57 | 58 | - name: start k3d 59 | run: | 60 | docker version 61 | k3d cluster create operator --agents 1 --image rancher/k3s:${K3S_VERSION} 62 | JSONPATH='{range .items[*]}{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status};{end}{end}'; 63 | until kubectl get nodes -o jsonpath="$JSONPATH" 2>&1 | grep -q "Ready=True"; do 64 | sleep 1; 65 | done 66 | 67 | # Dump cluster info 68 | kubectl cluster-info 69 | which kubectl 70 | kubectl version 71 | kubectl describe node k3d-operator-server-0 72 | kubectl describe node k3d-operator-agent-0 73 | 74 | 75 | # k3d integration tests 76 | - name: In cluster integration tests 77 | run: | 78 | export GNUPGHOME="$(mktemp -d)" 79 | cat >$GNUPGHOME/foo < config/samples/isindir_v1alpha3_sopssecret.enc.yaml 107 | file config/samples/isindir_v1alpha3_sopssecret.enc.yaml 108 | echo "=====================================" 109 | cat config/samples/isindir_v1alpha3_sopssecret.enc.yaml 110 | echo "=====================================" 111 | kubectl apply -f config/samples/isindir_v1alpha3_sopssecret.enc.yaml --namespace sops 112 | sleep 3 113 | nohup make run & 114 | sleep 150 115 | kubectl get sops --namespace sops 116 | echo 117 | kubectl get secrets --namespace sops 118 | echo 119 | export SECRETS_NUMBER=$( kubectl get secrets --namespace sops --no-headers \ 120 | | awk '$0 !~ /default-token/ { print $1; }' \ 121 | | wc -l ) 122 | if [[ $SECRETS_NUMBER -ne 5 ]]; then 123 | echo "Expected number of secrets in sops namespace is 5 - Failed" 124 | tail -40 nohup.out 125 | exit 1 126 | fi 127 | # Check specific k8s for amount of data entries 128 | ## my-secret-name-0 129 | export DATA_ENTRIES=$( kubectl get secrets my-secret-name-0 --namespace sops --no-headers \ 130 | | awk '{print $3}' ) 131 | if [[ $DATA_ENTRIES -ne 2 ]]; then 132 | echo "Expected number of data entries in my-secret-name-0 is 2 - Failed" 133 | tail -40 nohup.out 134 | exit 1 135 | fi 136 | ## my-secret-name-1 137 | export DATA_ENTRIES=$( kubectl get secrets my-secret-name-1 --namespace sops --no-headers \ 138 | | awk '{print $3}' ) 139 | if [[ $DATA_ENTRIES -ne 3 ]]; then 140 | echo "Expected number of data entries in my-secret-name-1 is 3 - Failed" 141 | tail -40 nohup.out 142 | exit 1 143 | fi 144 | ## my-secret-name-2 145 | export DATA_ENTRIES=$( kubectl get secrets my-secret-name-2 --namespace sops --no-headers \ 146 | | awk '{print $3}' ) 147 | if [[ $DATA_ENTRIES -ne 2 ]]; then 148 | echo "Expected number of data entries in my-secret-name-2 is 2 - Failed" 149 | tail -40 nohup.out 150 | exit 1 151 | fi 152 | # Delete to check GC works 153 | kubectl delete -f config/samples/isindir_v1alpha3_sopssecret.enc.yaml --namespace sops 154 | sleep 15 155 | kubectl get sops --namespace sops 156 | echo 157 | kubectl get secrets --namespace sops 158 | echo 159 | export SECRETS_NUMBER=$( kubectl get secrets --namespace sops \ 160 | | awk '$0!~/default-token/ && $0!~/NAME/ { print $1; }' \ 161 | | wc -l ) 162 | if [[ $SECRETS_NUMBER -ne 0 ]]; then 163 | echo "Expected number of secrets in sops namespace is 0 - Failed" 164 | exit 1 165 | fi 166 | rm -fr $GNUPGHOME 167 | 168 | - name: Set up Docker variables 169 | id: set_variable 170 | run: | 171 | echo "IMAGE_FULL_NAME=$(make image_full_name)" >> $GITHUB_ENV 172 | echo "IMAGE_LATEST_NAME=$(make image_latest_name)" >> $GITHUB_ENV 173 | echo "IMAGE_CACHE_NAME=$(make image_cache_name)" >> $GITHUB_ENV 174 | 175 | # UPDATE_HERE 176 | # https://github.com/docker/setup-qemu-action/releases 177 | - name: Set up QEMU 178 | uses: docker/setup-qemu-action@v3 179 | 180 | # UPDATE_HERE 181 | # https://github.com/docker/setup-buildx-action/releases 182 | - name: Set up Docker Buildx 183 | uses: docker/setup-buildx-action@v3 184 | 185 | # UPDATE_HERE 186 | # https://github.com/docker/login-action/releases 187 | - name: Log in to Docker Hub 188 | uses: docker/login-action@v3 189 | with: 190 | username: ${{ secrets.DOCKERHUB_USERNAME }} 191 | password: ${{ secrets.DOCKERHUB_PASS }} 192 | 193 | # UPDATE_HERE 194 | # https://github.com/docker/build-push-action/releases 195 | - name: Docker build 196 | uses: docker/build-push-action@v6 197 | with: 198 | context: . 199 | push: false 200 | tags: ${{ env.IMAGE_LATEST_NAME }},${{ env.IMAGE_FULL_NAME }} 201 | platforms: linux/amd64,linux/arm64 202 | cache-from: type=registry,ref=${{ env.IMAGE_CACHE_NAME }} 203 | cache-to: type=registry,ref=${{ env.IMAGE_CACHE_NAME }},mode=max 204 | -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: release 2 | on: 3 | workflow_dispatch: 4 | push: 5 | branches: 6 | - 'master' 7 | 8 | jobs: 9 | build: 10 | 11 | name: Release 12 | environment: ci-cd 13 | # UPDATE_HERE 14 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on 15 | runs-on: ubuntu-24.04 16 | 17 | steps: 18 | 19 | # UPDATE_HERE 20 | # https://github.com/actions/checkout/releases 21 | - uses: actions/checkout@v4 22 | name: Check out code 23 | with: 24 | fetch-tags: 'true' 25 | fetch-depth: 0 26 | 27 | - name: Set up Docker variables 28 | id: set_variables 29 | run: | 30 | VERSION=$(make image_tag) 31 | echo "SOPS_SEC_OPERATOR_VERSION=$VERSION" >> $GITHUB_ENV 32 | SKIP_RELEASE=$(git tag -l "${VERSION}") 33 | echo "IMAGE_FULL_NAME=$(make image_full_name)" >> $GITHUB_ENV 34 | echo "IMAGE_LATEST_NAME=$(make image_latest_name)" >> $GITHUB_ENV 35 | echo "IMAGE_CACHE_NAME=$(make image_cache_name)" >> $GITHUB_ENV 36 | echo "SKIP_RELEASE=${SKIP_RELEASE}" >> $GITHUB_ENV 37 | 38 | - name: Skip release 39 | if: env.SKIP_RELEASE != '' 40 | run: | 41 | echo "Skip release, tag found for ${VERSION}" 42 | 43 | # UPDATE_HERE 44 | # https://github.com/asdf-vm/actions/releases 45 | # https://github.com/asdf-vm/actions/issues/594 46 | - name: Install asdf tools 47 | if: env.SKIP_RELEASE == '' 48 | # uses: asdf-vm/actions/install@v3 49 | uses: asdf-vm/actions/install@9cd779f40fe38688dd19505ccbc4eaaf018b44e7 50 | with: 51 | asdf_version: 0.16.7 52 | 53 | # UPDATE_HERE 54 | # https://github.com/docker/setup-qemu-action/releases 55 | - name: Set up QEMU 56 | if: env.SKIP_RELEASE == '' 57 | uses: docker/setup-qemu-action@v3 58 | 59 | # UPDATE_HERE 60 | # https://github.com/docker/setup-buildx-action/releases 61 | - name: Set up Docker Buildx 62 | if: env.SKIP_RELEASE == '' 63 | uses: docker/setup-buildx-action@v3 64 | 65 | # UPDATE_HERE 66 | # https://github.com/docker/login-action/releases 67 | - name: Log in to Docker Hub 68 | if: env.SKIP_RELEASE == '' 69 | uses: docker/login-action@v3 70 | with: 71 | username: ${{ secrets.DOCKERHUB_USERNAME }} 72 | password: ${{ secrets.DOCKERHUB_PASS }} 73 | 74 | - name: Tag and release 75 | if: env.SKIP_RELEASE == '' 76 | env: 77 | GITHUB_TOKEN: ${{ secrets.GH_TOKEN }} 78 | run: | 79 | git tag "${SOPS_SEC_OPERATOR_VERSION}" 80 | git push origin --tags 81 | git-chglog "${SOPS_SEC_OPERATOR_VERSION}" > chglog.tmp 82 | gh release create "${SOPS_SEC_OPERATOR_VERSION}" -F chglog.tmp 83 | 84 | # UPDATE_HERE 85 | # https://github.com/docker/build-push-action/releases 86 | - name: Docker build 87 | if: env.SKIP_RELEASE == '' 88 | uses: docker/build-push-action@v6 89 | with: 90 | context: . 91 | push: true 92 | tags: ${{ env.IMAGE_LATEST_NAME }},${{ env.IMAGE_FULL_NAME }} 93 | platforms: linux/amd64,linux/arm64 94 | cache-from: type=registry,ref=${{ env.IMAGE_CACHE_NAME }} 95 | cache-to: type=registry,ref=${{ env.IMAGE_CACHE_NAME }},mode=max 96 | -------------------------------------------------------------------------------- /.github/workflows/reviewdog.yaml: -------------------------------------------------------------------------------- 1 | name: reviewdog 2 | on: [pull_request] 3 | 4 | jobs: 5 | golangci-lint: 6 | name: runner / golangci-lint 7 | # UPDATE_HERE 8 | # https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idruns-on 9 | runs-on: ubuntu-24.04 10 | steps: 11 | # UPDATE_HERE 12 | # https://github.com/actions/checkout/releases 13 | - name: Check out code into the Go module directory 14 | uses: actions/checkout@v4 15 | 16 | # UPDATE_HERE 17 | # https://github.com/reviewdog/action-golangci-lint/releases 18 | - name: golangci-lint 19 | uses: reviewdog/action-golangci-lint@v2 20 | with: 21 | golangci_lint_flags: "--timeout=4m" 22 | 23 | # UPDATE_HERE 24 | # https://github.com/reviewdog/action-actionlint/releases 25 | - name: action-lint 26 | uses: reviewdog/action-actionlint@v1 27 | with: 28 | actionlint_flags: "actionlint -shellcheck=" 29 | 30 | # UPDATE_HERE 31 | # https://github.com/reviewdog/action-hadolint/releases 32 | - name: docker hadolint 33 | uses: reviewdog/action-hadolint@v1 34 | with: 35 | hadolint_flags: --trusted-registry docker.io 36 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # UPDATE_HERE: at least helm-docs 2 | repos: 3 | - repo: https://github.com/norwoodj/helm-docs 4 | # https://github.com/norwoodj/helm-docs/releases 5 | rev: v1.14.2 6 | hooks: 7 | - id: helm-docs 8 | args: 9 | # Make the tool search for charts only under the `example-charts` directory 10 | - --chart-search-root=chart/helm3/ 11 | # The `./` makes it relative to the chart-search-root set above 12 | - --template-files=./README.md.gotmpl 13 | - repo: https://github.com/pre-commit/pre-commit-hooks 14 | # https://github.com/pre-commit/pre-commit-hooks/releases 15 | rev: v5.0.0 16 | hooks: 17 | - id: check-symlinks 18 | - id: check-merge-conflict 19 | - id: detect-private-key 20 | - id: end-of-file-fixer 21 | - id: trailing-whitespace 22 | -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | # UPDATE_HERE 2 | # https://github.com/kubernetes-sigs/kubebuilder/releases 3 | kubebuilder 4.5.2 4 | # https://golang.org/dl/ 5 | golang 1.24.3 6 | # https://github.com/mozilla/sops/releases 7 | sops 3.10.2 8 | # https://github.com/kubernetes-sigs/kustomize/releases 9 | kustomize 5.6.0 10 | # https://github.com/rancher/k3d/releases 11 | k3d 5.8.3 12 | # https://github.com/kubernetes/kubernetes/releases 13 | kubectl 1.33.0 14 | # https://github.com/helm/helm/releases 15 | helm 3.17.3 16 | # https://github.com/norwoodj/helm-docs/releases 17 | helm-docs 1.14.2 18 | # https://github.com/yannh/kubeconform/releases 19 | kubeconform 0.7.0 20 | # https://github.com/git-chglog/git-chglog/releases 21 | git-chglog 0.15.4 22 | # https://github.com/golangci/golangci-lint/releases 23 | golangci-lint 2.1.6 24 | # https://github.com/cli/cli/releases 25 | github-cli 2.72.0 26 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ############################################################ 2 | # https://wiki.ubuntu.com/Releases 3 | # https://hub.docker.com/_/ubuntu/tags?page=1&name=plucky 4 | # UPDATE_HERE 5 | FROM ubuntu:plucky-20250415 AS asdf-builder 6 | 7 | # UPDATE_HERE 8 | # https://github.com/asdf-vm/asdf/releases 9 | ARG ASDF_VERSION=v0.16.7 10 | 11 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 12 | 13 | # Install build tools 14 | RUN apt-get -y update \ 15 | && apt-get -y install build-essential \ 16 | && apt-get -y install autoconf automake gdb git libffi-dev zlib1g-dev libssl-dev curl wget \ 17 | && apt-get clean && rm -rf /var/lib/apt/lists/* 18 | 19 | # Install asdf 20 | WORKDIR /usr/local 21 | RUN git config --global user.email "you@example.com" \ 22 | && git config --global user.name "Your Name" \ 23 | && git config --global init.defaultBranch ${ASDF_VERSION} \ 24 | && git config --global pull.rebase true \ 25 | && git init \ 26 | && git add . \ 27 | && git commit -m'Initial commit' && git remote add origin https://github.com/asdf-vm/asdf.git \ 28 | && git pull origin ${ASDF_VERSION} --allow-unrelated-histories \ 29 | && rm -fr .git ~/.gitconfig 30 | 31 | # Install project build tools and linters using asdf 32 | WORKDIR /root 33 | COPY .tool-versions . 34 | 35 | RUN awk '$0 !~ /^#/ {print $1}' .tool-versions|xargs -I{} asdf plugin add {} \ 36 | && asdf install && asdf reshim 37 | ENV PATH="/root/.asdf/shims:/root/.asdf/bin:$PATH" 38 | 39 | # Compile source code 40 | WORKDIR /workspace 41 | # Copy the Go Modules manifests 42 | COPY go.mod go.mod 43 | COPY go.sum go.sum 44 | # cache deps before building and copying source so that we don't need to re-download as much 45 | # and so that source changes don't invalidate our downloaded layer 46 | RUN go mod download 47 | 48 | # Copy the go source 49 | COPY cmd/ cmd/ 50 | COPY api/ api/ 51 | COPY internal/ internal/ 52 | 53 | # Build (GOARCH=amd64) 54 | RUN CGO_ENABLED=0 GO111MODULE=on go build -a -o manager cmd/main.go 55 | 56 | ############################################################ 57 | # UPDATE_HERE 58 | FROM ubuntu:plucky-20250415 59 | 60 | # Install build tools 61 | RUN apt-get -y update \ 62 | && apt-get -y upgrade \ 63 | && apt-get -y install --no-install-recommends gnupg2 ca-certificates \ 64 | && apt-get clean && rm -rf /var/lib/apt/lists/* 65 | 66 | WORKDIR /usr/local/bin 67 | COPY --from=asdf-builder /workspace/manager . 68 | 69 | RUN useradd --create-home --user-group nonroot 70 | USER nonroot:nonroot 71 | 72 | ENTRYPOINT ["/usr/local/bin/manager"] 73 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | domain: github.com 2 | layout: 3 | - go.kubebuilder.io/v4 4 | projectName: sops-secrets-operator 5 | repo: github.com/isindir/sops-secrets-operator 6 | # UPDATE_HERE 7 | resources: 8 | - api: 9 | crdVersion: v1 10 | namespaced: true 11 | controller: true 12 | domain: github.com 13 | group: isindir 14 | kind: SopsSecret 15 | path: github.com/isindir/sops-secrets-operator/api/v1alpha3 16 | version: v1alpha3 17 | - api: 18 | crdVersion: v1 19 | namespaced: true 20 | domain: github.com 21 | group: isindir 22 | kind: SopsSecret 23 | path: github.com/isindir/sops-secrets-operator/api/v1alpha2 24 | version: v1alpha2 25 | - api: 26 | crdVersion: v1 27 | namespaced: true 28 | domain: github.com 29 | group: isindir 30 | kind: SopsSecret 31 | path: github.com/isindir/sops-secrets-operator/api/v1alpha1 32 | version: v1alpha1 33 | version: "3" 34 | -------------------------------------------------------------------------------- /api/v1alpha1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 | 5 | // Package v1alpha1 contains API Schema definitions for the isindir v1alpha1 API group 6 | // +kubebuilder:object:generate=true 7 | // +groupName=isindir.github.com 8 | package v1alpha1 9 | 10 | import ( 11 | "k8s.io/apimachinery/pkg/runtime/schema" 12 | "sigs.k8s.io/controller-runtime/pkg/scheme" 13 | ) 14 | 15 | var ( 16 | // GroupVersion is group version used to register these objects 17 | GroupVersion = schema.GroupVersion{Group: "isindir.github.com", Version: "v1alpha1"} 18 | 19 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 20 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 21 | 22 | // AddToScheme adds the types in this group-version to the given scheme. 23 | AddToScheme = SchemeBuilder.AddToScheme 24 | ) 25 | -------------------------------------------------------------------------------- /api/v1alpha1/sopssecret_types.go: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 | 5 | package v1alpha1 6 | 7 | import ( 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 12 | // For upstream reference, see https://github.com/mozilla/sops/blob/master/stores/stores.go 13 | 14 | // SopsSecretSpec defines the desired state of SopsSecret 15 | type SopsSecretSpec struct { 16 | // Secrets template is a list of definitions to create Kubernetes Secrets 17 | //+kubebuilder:validation:MinItems=1 18 | //+required 19 | SecretsTemplate []SopsSecretTemplate `json:"secret_templates"` 20 | } 21 | 22 | // SopsSecretTemplate defines the map of secrets to create 23 | type SopsSecretTemplate struct { 24 | // Name of the Kubernetes secret to create 25 | //+required 26 | Name string `json:"name"` 27 | 28 | // Annotations to apply to Kubernetes secret 29 | //+optional 30 | Annotations map[string]string `json:"annotations,omitempty"` 31 | 32 | // Labels to apply to Kubernetes secret 33 | //+optional 34 | Labels map[string]string `json:"labels,omitempty"` 35 | 36 | // Kubernetes secret type. Default: Opauqe. Possible values: Opauqe, 37 | // kubernetes.io/service-account-token, kubernetes.io/dockercfg, 38 | // kubernetes.io/dockerconfigjson, kubernetes.io/basic-auth, 39 | // kubernetes.io/ssh-auth, kubernetes.io/tls, bootstrap.kubernetes.io/token 40 | //+optional 41 | Type string `json:"type,omitempty"` 42 | 43 | // Data map to use in Kubernetes secret (equivalent to Kubernetes Secret object stringData, please see for more 44 | // information: https://kubernetes.io/docs/concepts/configuration/secret/#overview-of-secrets) 45 | Data map[string]string `json:"data"` 46 | } 47 | 48 | // KmsDataItem defines AWS KMS specific encryption details 49 | type KmsDataItem struct { 50 | // Arn - KMS key ARN to use 51 | //+optional 52 | Arn string `json:"arn,omitempty"` 53 | 54 | //+optional 55 | EncryptedKey string `json:"enc,omitempty"` 56 | // Object creation date 57 | //+optional 58 | CreationDate string `json:"created_at,omitempty"` 59 | //+optional 60 | AwsProfile string `json:"aws_profile,omitempty"` 61 | } 62 | 63 | // PgpDataItem defines PGP specific encryption details 64 | type PgpDataItem struct { 65 | //+optional 66 | EncryptedKey string `json:"enc,omitempty"` 67 | 68 | // Object creation date 69 | //+optional 70 | CreationDate string `json:"created_at,omitempty"` 71 | // PGP FingerPrint of the key which can be used for decryption 72 | //+optional 73 | FingerPrint string `json:"fp,omitempty"` 74 | } 75 | 76 | // AzureKmsItem defines Azure Keyvault Key specific encryption details 77 | type AzureKmsItem struct { 78 | // Azure KMS vault URL 79 | //+optional 80 | VaultURL string `json:"vault_url,omitempty"` 81 | //+optional 82 | KeyName string `json:"name,omitempty"` 83 | //+optional 84 | Version string `json:"version,omitempty"` 85 | //+optional 86 | EncryptedKey string `json:"enc,omitempty"` 87 | // Object creation date 88 | //+optional 89 | CreationDate string `json:"created_at,omitempty"` 90 | } 91 | 92 | // GcpKmsDataItem defines GCP KMS Key specific encryption details 93 | type GcpKmsDataItem struct { 94 | //+optional 95 | VaultURL string `json:"resource_id,omitempty"` 96 | //+optional 97 | EncryptedKey string `json:"enc,omitempty"` 98 | // Object creation date 99 | //+optional 100 | CreationDate string `json:"created_at,omitempty"` 101 | } 102 | 103 | // SopsMetadata defines the encryption details 104 | type SopsMetadata struct { 105 | // Aws KMS configuration 106 | //+optional 107 | AwsKms []KmsDataItem `json:"kms,omitempty"` 108 | 109 | // PGP configuration 110 | //+optional 111 | Pgp []PgpDataItem `json:"pgp,omitempty"` 112 | 113 | // Azure KMS configuration 114 | //+optional 115 | AzureKms []AzureKmsItem `json:"azure_kv,omitempty"` 116 | 117 | // Gcp KMS configuration 118 | //+optional 119 | GcpKms []GcpKmsDataItem `json:"gcp_kms,omitempty"` 120 | 121 | // Mac - sops setting 122 | //+optional 123 | Mac string `json:"mac,omitempty"` 124 | 125 | // LastModified date when SopsSecret was last modified 126 | //+optional 127 | LastModified string `json:"lastmodified,omitempty"` 128 | 129 | // Version of the sops tool used to encrypt SopsSecret 130 | //+optional 131 | Version string `json:"version,omitempty"` 132 | 133 | // Suffix used to encrypt SopsSecret resource 134 | //+optional 135 | EncryptedSuffix string `json:"encrypted_suffix,omitempty"` 136 | } 137 | 138 | // SopsSecretStatus defines the observed state of SopsSecret 139 | type SopsSecretStatus struct { 140 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 141 | // Important: Run "make" to regenerate code after modifying this file 142 | // Add custom validation using kubebuilder tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html 143 | } 144 | 145 | //+kubebuilder:object:root=true 146 | //+kubebuilder:subresource:status 147 | 148 | // SopsSecret is the Schema for the sopssecrets API 149 | // +kubebuilder:resource:shortName=sops,scope=Namespaced 150 | // +kubebuilder:deprecatedversion 151 | // +kubebuilder:subresource:status 152 | type SopsSecret struct { 153 | metav1.TypeMeta `json:",inline"` 154 | metav1.ObjectMeta `json:"metadata,omitempty"` 155 | 156 | // SopsSecret Spec definition 157 | Spec SopsSecretSpec `json:"spec,omitempty"` 158 | // SopsSecret Status information 159 | Status SopsSecretStatus `json:"status,omitempty"` 160 | // SopsSecret metadata 161 | Sops SopsMetadata `json:"sops,omitempty"` 162 | } 163 | 164 | //+kubebuilder:object:root=true 165 | 166 | // SopsSecretList contains a list of SopsSecret 167 | type SopsSecretList struct { 168 | metav1.TypeMeta `json:",inline"` 169 | metav1.ListMeta `json:"metadata,omitempty"` 170 | Items []SopsSecret `json:"items"` 171 | } 172 | 173 | func init() { 174 | SchemeBuilder.Register(&SopsSecret{}, &SopsSecretList{}) 175 | } 176 | -------------------------------------------------------------------------------- /api/v1alpha1/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 6 | 7 | // Code generated by controller-gen. DO NOT EDIT. 8 | 9 | package v1alpha1 10 | 11 | import ( 12 | runtime "k8s.io/apimachinery/pkg/runtime" 13 | ) 14 | 15 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 16 | func (in *AzureKmsItem) DeepCopyInto(out *AzureKmsItem) { 17 | *out = *in 18 | } 19 | 20 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureKmsItem. 21 | func (in *AzureKmsItem) DeepCopy() *AzureKmsItem { 22 | if in == nil { 23 | return nil 24 | } 25 | out := new(AzureKmsItem) 26 | in.DeepCopyInto(out) 27 | return out 28 | } 29 | 30 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 31 | func (in *GcpKmsDataItem) DeepCopyInto(out *GcpKmsDataItem) { 32 | *out = *in 33 | } 34 | 35 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GcpKmsDataItem. 36 | func (in *GcpKmsDataItem) DeepCopy() *GcpKmsDataItem { 37 | if in == nil { 38 | return nil 39 | } 40 | out := new(GcpKmsDataItem) 41 | in.DeepCopyInto(out) 42 | return out 43 | } 44 | 45 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 46 | func (in *KmsDataItem) DeepCopyInto(out *KmsDataItem) { 47 | *out = *in 48 | } 49 | 50 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KmsDataItem. 51 | func (in *KmsDataItem) DeepCopy() *KmsDataItem { 52 | if in == nil { 53 | return nil 54 | } 55 | out := new(KmsDataItem) 56 | in.DeepCopyInto(out) 57 | return out 58 | } 59 | 60 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 61 | func (in *PgpDataItem) DeepCopyInto(out *PgpDataItem) { 62 | *out = *in 63 | } 64 | 65 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PgpDataItem. 66 | func (in *PgpDataItem) DeepCopy() *PgpDataItem { 67 | if in == nil { 68 | return nil 69 | } 70 | out := new(PgpDataItem) 71 | in.DeepCopyInto(out) 72 | return out 73 | } 74 | 75 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 76 | func (in *SopsMetadata) DeepCopyInto(out *SopsMetadata) { 77 | *out = *in 78 | if in.AwsKms != nil { 79 | in, out := &in.AwsKms, &out.AwsKms 80 | *out = make([]KmsDataItem, len(*in)) 81 | copy(*out, *in) 82 | } 83 | if in.Pgp != nil { 84 | in, out := &in.Pgp, &out.Pgp 85 | *out = make([]PgpDataItem, len(*in)) 86 | copy(*out, *in) 87 | } 88 | if in.AzureKms != nil { 89 | in, out := &in.AzureKms, &out.AzureKms 90 | *out = make([]AzureKmsItem, len(*in)) 91 | copy(*out, *in) 92 | } 93 | if in.GcpKms != nil { 94 | in, out := &in.GcpKms, &out.GcpKms 95 | *out = make([]GcpKmsDataItem, len(*in)) 96 | copy(*out, *in) 97 | } 98 | } 99 | 100 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsMetadata. 101 | func (in *SopsMetadata) DeepCopy() *SopsMetadata { 102 | if in == nil { 103 | return nil 104 | } 105 | out := new(SopsMetadata) 106 | in.DeepCopyInto(out) 107 | return out 108 | } 109 | 110 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 111 | func (in *SopsSecret) DeepCopyInto(out *SopsSecret) { 112 | *out = *in 113 | out.TypeMeta = in.TypeMeta 114 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 115 | in.Spec.DeepCopyInto(&out.Spec) 116 | out.Status = in.Status 117 | in.Sops.DeepCopyInto(&out.Sops) 118 | } 119 | 120 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsSecret. 121 | func (in *SopsSecret) DeepCopy() *SopsSecret { 122 | if in == nil { 123 | return nil 124 | } 125 | out := new(SopsSecret) 126 | in.DeepCopyInto(out) 127 | return out 128 | } 129 | 130 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 131 | func (in *SopsSecret) DeepCopyObject() runtime.Object { 132 | if c := in.DeepCopy(); c != nil { 133 | return c 134 | } 135 | return nil 136 | } 137 | 138 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 139 | func (in *SopsSecretList) DeepCopyInto(out *SopsSecretList) { 140 | *out = *in 141 | out.TypeMeta = in.TypeMeta 142 | in.ListMeta.DeepCopyInto(&out.ListMeta) 143 | if in.Items != nil { 144 | in, out := &in.Items, &out.Items 145 | *out = make([]SopsSecret, len(*in)) 146 | for i := range *in { 147 | (*in)[i].DeepCopyInto(&(*out)[i]) 148 | } 149 | } 150 | } 151 | 152 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsSecretList. 153 | func (in *SopsSecretList) DeepCopy() *SopsSecretList { 154 | if in == nil { 155 | return nil 156 | } 157 | out := new(SopsSecretList) 158 | in.DeepCopyInto(out) 159 | return out 160 | } 161 | 162 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 163 | func (in *SopsSecretList) DeepCopyObject() runtime.Object { 164 | if c := in.DeepCopy(); c != nil { 165 | return c 166 | } 167 | return nil 168 | } 169 | 170 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 171 | func (in *SopsSecretSpec) DeepCopyInto(out *SopsSecretSpec) { 172 | *out = *in 173 | if in.SecretsTemplate != nil { 174 | in, out := &in.SecretsTemplate, &out.SecretsTemplate 175 | *out = make([]SopsSecretTemplate, len(*in)) 176 | for i := range *in { 177 | (*in)[i].DeepCopyInto(&(*out)[i]) 178 | } 179 | } 180 | } 181 | 182 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsSecretSpec. 183 | func (in *SopsSecretSpec) DeepCopy() *SopsSecretSpec { 184 | if in == nil { 185 | return nil 186 | } 187 | out := new(SopsSecretSpec) 188 | in.DeepCopyInto(out) 189 | return out 190 | } 191 | 192 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 193 | func (in *SopsSecretStatus) DeepCopyInto(out *SopsSecretStatus) { 194 | *out = *in 195 | } 196 | 197 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsSecretStatus. 198 | func (in *SopsSecretStatus) DeepCopy() *SopsSecretStatus { 199 | if in == nil { 200 | return nil 201 | } 202 | out := new(SopsSecretStatus) 203 | in.DeepCopyInto(out) 204 | return out 205 | } 206 | 207 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 208 | func (in *SopsSecretTemplate) DeepCopyInto(out *SopsSecretTemplate) { 209 | *out = *in 210 | if in.Annotations != nil { 211 | in, out := &in.Annotations, &out.Annotations 212 | *out = make(map[string]string, len(*in)) 213 | for key, val := range *in { 214 | (*out)[key] = val 215 | } 216 | } 217 | if in.Labels != nil { 218 | in, out := &in.Labels, &out.Labels 219 | *out = make(map[string]string, len(*in)) 220 | for key, val := range *in { 221 | (*out)[key] = val 222 | } 223 | } 224 | if in.Data != nil { 225 | in, out := &in.Data, &out.Data 226 | *out = make(map[string]string, len(*in)) 227 | for key, val := range *in { 228 | (*out)[key] = val 229 | } 230 | } 231 | } 232 | 233 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsSecretTemplate. 234 | func (in *SopsSecretTemplate) DeepCopy() *SopsSecretTemplate { 235 | if in == nil { 236 | return nil 237 | } 238 | out := new(SopsSecretTemplate) 239 | in.DeepCopyInto(out) 240 | return out 241 | } 242 | -------------------------------------------------------------------------------- /api/v1alpha2/groupversion_info.go: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 | 5 | // Package v1alpha2 contains API Schema definitions for the isindir v1alpha2 API group 6 | // +kubebuilder:object:generate=true 7 | // +groupName=isindir.github.com 8 | package v1alpha2 9 | 10 | import ( 11 | "k8s.io/apimachinery/pkg/runtime/schema" 12 | "sigs.k8s.io/controller-runtime/pkg/scheme" 13 | ) 14 | 15 | var ( 16 | // GroupVersion is group version used to register these objects 17 | GroupVersion = schema.GroupVersion{Group: "isindir.github.com", Version: "v1alpha2"} 18 | 19 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 20 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 21 | 22 | // AddToScheme adds the types in this group-version to the given scheme. 23 | AddToScheme = SchemeBuilder.AddToScheme 24 | ) 25 | -------------------------------------------------------------------------------- /api/v1alpha2/sopssecret_types.go: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 | 5 | package v1alpha2 6 | 7 | import ( 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 12 | // For upstream reference, see https://github.com/mozilla/sops/blob/master/stores/stores.go 13 | 14 | // SopsSecretSpec defines the desired state of SopsSecret 15 | type SopsSecretSpec struct { 16 | // Secrets template is a list of definitions to create Kubernetes Secrets 17 | //+kubebuilder:validation:MinItems=1 18 | //+required 19 | SecretsTemplate []SopsSecretTemplate `json:"secretTemplates"` 20 | } 21 | 22 | // SopsSecretTemplate defines the map of secrets to create 23 | type SopsSecretTemplate struct { 24 | // Name of the Kubernetes secret to create 25 | //+required 26 | Name string `json:"name"` 27 | 28 | // Annotations to apply to Kubernetes secret 29 | //+optional 30 | Annotations map[string]string `json:"annotations,omitempty"` 31 | 32 | // Labels to apply to Kubernetes secret 33 | //+optional 34 | Labels map[string]string `json:"labels,omitempty"` 35 | 36 | // Kubernetes secret type. Default: Opauqe. Possible values: Opauqe, 37 | // kubernetes.io/service-account-token, kubernetes.io/dockercfg, 38 | // kubernetes.io/dockerconfigjson, kubernetes.io/basic-auth, 39 | // kubernetes.io/ssh-auth, kubernetes.io/tls, bootstrap.kubernetes.io/token 40 | //+optional 41 | Type string `json:"type,omitempty"` 42 | 43 | // Data map to use in Kubernetes secret (equivalent to Kubernetes Secret object stringData, please see for more 44 | // information: https://kubernetes.io/docs/concepts/configuration/secret/#overview-of-secrets) 45 | Data map[string]string `json:"data"` 46 | } 47 | 48 | // KmsDataItem defines AWS KMS specific encryption details 49 | type KmsDataItem struct { 50 | // Arn - KMS key ARN to use 51 | //+optional 52 | Arn string `json:"arn,omitempty"` 53 | // AWS Iam Role 54 | //+optional 55 | Role string `json:"role,omitempty"` 56 | 57 | //+optional 58 | EncryptedKey string `json:"enc,omitempty"` 59 | // Object creation date 60 | //+optional 61 | CreationDate string `json:"created_at,omitempty"` 62 | //+optional 63 | AwsProfile string `json:"aws_profile,omitempty"` 64 | } 65 | 66 | // PgpDataItem defines PGP specific encryption details 67 | type PgpDataItem struct { 68 | //+optional 69 | EncryptedKey string `json:"enc,omitempty"` 70 | 71 | // Object creation date 72 | //+optional 73 | CreationDate string `json:"created_at,omitempty"` 74 | // PGP FingerPrint of the key which can be used for decryption 75 | //+optional 76 | FingerPrint string `json:"fp,omitempty"` 77 | } 78 | 79 | // AzureKmsItem defines Azure Keyvault Key specific encryption details 80 | type AzureKmsItem struct { 81 | // Azure KMS vault URL 82 | //+optional 83 | VaultURL string `json:"vault_url,omitempty"` 84 | //+optional 85 | KeyName string `json:"name,omitempty"` 86 | //+optional 87 | Version string `json:"version,omitempty"` 88 | //+optional 89 | EncryptedKey string `json:"enc,omitempty"` 90 | // Object creation date 91 | //+optional 92 | CreationDate string `json:"created_at,omitempty"` 93 | } 94 | 95 | // AgeItem defines FiloSottile/age specific encryption details 96 | type AgeItem struct { 97 | // Recipient which private key can be used for decription 98 | //+optional 99 | Recipient string `json:"recipient,omitempty"` 100 | //+optional 101 | EncryptedKey string `json:"enc,omitempty"` 102 | } 103 | 104 | // HcVaultItem defines Hashicorp Vault Key specific encryption details 105 | type HcVaultItem struct { 106 | //+optional 107 | VaultAddress string `json:"vault_address,omitempty"` 108 | //+optional 109 | EnginePath string `json:"engine_path,omitempty"` 110 | //+optional 111 | KeyName string `json:"key_name,omitempty"` 112 | //+optional 113 | CreationDate string `json:"created_at,omitempty"` 114 | //+optional 115 | EncryptedKey string `json:"enc,omitempty"` 116 | } 117 | 118 | // GcpKmsDataItem defines GCP KMS Key specific encryption details 119 | type GcpKmsDataItem struct { 120 | //+optional 121 | VaultURL string `json:"resource_id,omitempty"` 122 | //+optional 123 | EncryptedKey string `json:"enc,omitempty"` 124 | // Object creation date 125 | //+optional 126 | CreationDate string `json:"created_at,omitempty"` 127 | } 128 | 129 | // SopsMetadata defines the encryption details 130 | type SopsMetadata struct { 131 | // Aws KMS configuration 132 | //+optional 133 | AwsKms []KmsDataItem `json:"kms,omitempty"` 134 | 135 | // PGP configuration 136 | //+optional 137 | Pgp []PgpDataItem `json:"pgp,omitempty"` 138 | 139 | // Azure KMS configuration 140 | //+optional 141 | AzureKms []AzureKmsItem `json:"azure_kv,omitempty"` 142 | 143 | // Hashicorp Vault KMS configurarion 144 | //+optional 145 | HcVault []HcVaultItem `json:"hc_vault,omitempty"` 146 | 147 | // Gcp KMS configuration 148 | //+optional 149 | GcpKms []GcpKmsDataItem `json:"gcp_kms,omitempty"` 150 | 151 | // Age configuration 152 | //+optional 153 | Age []AgeItem `json:"age,omitempty"` 154 | 155 | // Mac - sops setting 156 | //+optional 157 | Mac string `json:"mac,omitempty"` 158 | 159 | // LastModified date when SopsSecret was last modified 160 | //+optional 161 | LastModified string `json:"lastmodified,omitempty"` 162 | 163 | // Version of the sops tool used to encrypt SopsSecret 164 | //+optional 165 | Version string `json:"version,omitempty"` 166 | 167 | // Suffix used to encrypt SopsSecret resource 168 | //+optional 169 | EncryptedSuffix string `json:"encrypted_suffix,omitempty"` 170 | 171 | // Regex used to encrypt SopsSecret resource 172 | // This opstion should be used with more care, as it can make resource unapplicable to the cluster. 173 | //+optional 174 | EncryptedRegex string `json:"encrypted_regex,omitempty"` 175 | } 176 | 177 | // SopsSecretStatus defines the observed state of SopsSecret 178 | type SopsSecretStatus struct { 179 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 180 | // Important: Run "make" to regenerate code after modifying this file 181 | 182 | // SopsSecret status message 183 | //+optional 184 | Message string `json:"message,omitempty"` 185 | } 186 | 187 | //+kubebuilder:object:root=true 188 | //+kubebuilder:subresource:status 189 | 190 | // SopsSecret is the Schema for the sopssecrets API 191 | // +kubebuilder:resource:shortName=sops,scope=Namespaced 192 | // +kubebuilder:deprecatedversion 193 | // +kubebuilder:subresource:status 194 | // +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.message` 195 | type SopsSecret struct { 196 | metav1.TypeMeta `json:",inline"` 197 | metav1.ObjectMeta `json:"metadata,omitempty"` 198 | 199 | // SopsSecret Spec definition 200 | Spec SopsSecretSpec `json:"spec,omitempty"` 201 | // SopsSecret Status information 202 | Status SopsSecretStatus `json:"status,omitempty"` 203 | // SopsSecret metadata 204 | Sops SopsMetadata `json:"sops,omitempty"` 205 | } 206 | 207 | //+kubebuilder:object:root=true 208 | 209 | // SopsSecretList contains a list of SopsSecret 210 | type SopsSecretList struct { 211 | metav1.TypeMeta `json:",inline"` 212 | metav1.ListMeta `json:"metadata,omitempty"` 213 | Items []SopsSecret `json:"items"` 214 | } 215 | 216 | func init() { 217 | SchemeBuilder.Register(&SopsSecret{}, &SopsSecretList{}) 218 | } 219 | -------------------------------------------------------------------------------- /api/v1alpha2/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 6 | 7 | // Code generated by controller-gen. DO NOT EDIT. 8 | 9 | package v1alpha2 10 | 11 | import ( 12 | runtime "k8s.io/apimachinery/pkg/runtime" 13 | ) 14 | 15 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 16 | func (in *AgeItem) DeepCopyInto(out *AgeItem) { 17 | *out = *in 18 | } 19 | 20 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgeItem. 21 | func (in *AgeItem) DeepCopy() *AgeItem { 22 | if in == nil { 23 | return nil 24 | } 25 | out := new(AgeItem) 26 | in.DeepCopyInto(out) 27 | return out 28 | } 29 | 30 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 31 | func (in *AzureKmsItem) DeepCopyInto(out *AzureKmsItem) { 32 | *out = *in 33 | } 34 | 35 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureKmsItem. 36 | func (in *AzureKmsItem) DeepCopy() *AzureKmsItem { 37 | if in == nil { 38 | return nil 39 | } 40 | out := new(AzureKmsItem) 41 | in.DeepCopyInto(out) 42 | return out 43 | } 44 | 45 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 46 | func (in *GcpKmsDataItem) DeepCopyInto(out *GcpKmsDataItem) { 47 | *out = *in 48 | } 49 | 50 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GcpKmsDataItem. 51 | func (in *GcpKmsDataItem) DeepCopy() *GcpKmsDataItem { 52 | if in == nil { 53 | return nil 54 | } 55 | out := new(GcpKmsDataItem) 56 | in.DeepCopyInto(out) 57 | return out 58 | } 59 | 60 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 61 | func (in *HcVaultItem) DeepCopyInto(out *HcVaultItem) { 62 | *out = *in 63 | } 64 | 65 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HcVaultItem. 66 | func (in *HcVaultItem) DeepCopy() *HcVaultItem { 67 | if in == nil { 68 | return nil 69 | } 70 | out := new(HcVaultItem) 71 | in.DeepCopyInto(out) 72 | return out 73 | } 74 | 75 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 76 | func (in *KmsDataItem) DeepCopyInto(out *KmsDataItem) { 77 | *out = *in 78 | } 79 | 80 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KmsDataItem. 81 | func (in *KmsDataItem) DeepCopy() *KmsDataItem { 82 | if in == nil { 83 | return nil 84 | } 85 | out := new(KmsDataItem) 86 | in.DeepCopyInto(out) 87 | return out 88 | } 89 | 90 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 91 | func (in *PgpDataItem) DeepCopyInto(out *PgpDataItem) { 92 | *out = *in 93 | } 94 | 95 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PgpDataItem. 96 | func (in *PgpDataItem) DeepCopy() *PgpDataItem { 97 | if in == nil { 98 | return nil 99 | } 100 | out := new(PgpDataItem) 101 | in.DeepCopyInto(out) 102 | return out 103 | } 104 | 105 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 106 | func (in *SopsMetadata) DeepCopyInto(out *SopsMetadata) { 107 | *out = *in 108 | if in.AwsKms != nil { 109 | in, out := &in.AwsKms, &out.AwsKms 110 | *out = make([]KmsDataItem, len(*in)) 111 | copy(*out, *in) 112 | } 113 | if in.Pgp != nil { 114 | in, out := &in.Pgp, &out.Pgp 115 | *out = make([]PgpDataItem, len(*in)) 116 | copy(*out, *in) 117 | } 118 | if in.AzureKms != nil { 119 | in, out := &in.AzureKms, &out.AzureKms 120 | *out = make([]AzureKmsItem, len(*in)) 121 | copy(*out, *in) 122 | } 123 | if in.HcVault != nil { 124 | in, out := &in.HcVault, &out.HcVault 125 | *out = make([]HcVaultItem, len(*in)) 126 | copy(*out, *in) 127 | } 128 | if in.GcpKms != nil { 129 | in, out := &in.GcpKms, &out.GcpKms 130 | *out = make([]GcpKmsDataItem, len(*in)) 131 | copy(*out, *in) 132 | } 133 | if in.Age != nil { 134 | in, out := &in.Age, &out.Age 135 | *out = make([]AgeItem, len(*in)) 136 | copy(*out, *in) 137 | } 138 | } 139 | 140 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsMetadata. 141 | func (in *SopsMetadata) DeepCopy() *SopsMetadata { 142 | if in == nil { 143 | return nil 144 | } 145 | out := new(SopsMetadata) 146 | in.DeepCopyInto(out) 147 | return out 148 | } 149 | 150 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 151 | func (in *SopsSecret) DeepCopyInto(out *SopsSecret) { 152 | *out = *in 153 | out.TypeMeta = in.TypeMeta 154 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 155 | in.Spec.DeepCopyInto(&out.Spec) 156 | out.Status = in.Status 157 | in.Sops.DeepCopyInto(&out.Sops) 158 | } 159 | 160 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsSecret. 161 | func (in *SopsSecret) DeepCopy() *SopsSecret { 162 | if in == nil { 163 | return nil 164 | } 165 | out := new(SopsSecret) 166 | in.DeepCopyInto(out) 167 | return out 168 | } 169 | 170 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 171 | func (in *SopsSecret) DeepCopyObject() runtime.Object { 172 | if c := in.DeepCopy(); c != nil { 173 | return c 174 | } 175 | return nil 176 | } 177 | 178 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 179 | func (in *SopsSecretList) DeepCopyInto(out *SopsSecretList) { 180 | *out = *in 181 | out.TypeMeta = in.TypeMeta 182 | in.ListMeta.DeepCopyInto(&out.ListMeta) 183 | if in.Items != nil { 184 | in, out := &in.Items, &out.Items 185 | *out = make([]SopsSecret, len(*in)) 186 | for i := range *in { 187 | (*in)[i].DeepCopyInto(&(*out)[i]) 188 | } 189 | } 190 | } 191 | 192 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsSecretList. 193 | func (in *SopsSecretList) DeepCopy() *SopsSecretList { 194 | if in == nil { 195 | return nil 196 | } 197 | out := new(SopsSecretList) 198 | in.DeepCopyInto(out) 199 | return out 200 | } 201 | 202 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 203 | func (in *SopsSecretList) DeepCopyObject() runtime.Object { 204 | if c := in.DeepCopy(); c != nil { 205 | return c 206 | } 207 | return nil 208 | } 209 | 210 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 211 | func (in *SopsSecretSpec) DeepCopyInto(out *SopsSecretSpec) { 212 | *out = *in 213 | if in.SecretsTemplate != nil { 214 | in, out := &in.SecretsTemplate, &out.SecretsTemplate 215 | *out = make([]SopsSecretTemplate, len(*in)) 216 | for i := range *in { 217 | (*in)[i].DeepCopyInto(&(*out)[i]) 218 | } 219 | } 220 | } 221 | 222 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsSecretSpec. 223 | func (in *SopsSecretSpec) DeepCopy() *SopsSecretSpec { 224 | if in == nil { 225 | return nil 226 | } 227 | out := new(SopsSecretSpec) 228 | in.DeepCopyInto(out) 229 | return out 230 | } 231 | 232 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 233 | func (in *SopsSecretStatus) DeepCopyInto(out *SopsSecretStatus) { 234 | *out = *in 235 | } 236 | 237 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsSecretStatus. 238 | func (in *SopsSecretStatus) DeepCopy() *SopsSecretStatus { 239 | if in == nil { 240 | return nil 241 | } 242 | out := new(SopsSecretStatus) 243 | in.DeepCopyInto(out) 244 | return out 245 | } 246 | 247 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 248 | func (in *SopsSecretTemplate) DeepCopyInto(out *SopsSecretTemplate) { 249 | *out = *in 250 | if in.Annotations != nil { 251 | in, out := &in.Annotations, &out.Annotations 252 | *out = make(map[string]string, len(*in)) 253 | for key, val := range *in { 254 | (*out)[key] = val 255 | } 256 | } 257 | if in.Labels != nil { 258 | in, out := &in.Labels, &out.Labels 259 | *out = make(map[string]string, len(*in)) 260 | for key, val := range *in { 261 | (*out)[key] = val 262 | } 263 | } 264 | if in.Data != nil { 265 | in, out := &in.Data, &out.Data 266 | *out = make(map[string]string, len(*in)) 267 | for key, val := range *in { 268 | (*out)[key] = val 269 | } 270 | } 271 | } 272 | 273 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsSecretTemplate. 274 | func (in *SopsSecretTemplate) DeepCopy() *SopsSecretTemplate { 275 | if in == nil { 276 | return nil 277 | } 278 | out := new(SopsSecretTemplate) 279 | in.DeepCopyInto(out) 280 | return out 281 | } 282 | -------------------------------------------------------------------------------- /api/v1alpha3/groupversion_info.go: -------------------------------------------------------------------------------- 1 | // Package v1alpha3 contains API Schema definitions for the isindir v1alpha3 API group 2 | // +kubebuilder:object:generate=true 3 | // +groupName=isindir.github.com 4 | package v1alpha3 5 | 6 | import ( 7 | "k8s.io/apimachinery/pkg/runtime/schema" 8 | "sigs.k8s.io/controller-runtime/pkg/scheme" 9 | ) 10 | 11 | var ( 12 | // GroupVersion is group version used to register these objects 13 | GroupVersion = schema.GroupVersion{Group: "isindir.github.com", Version: "v1alpha3"} 14 | 15 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme 16 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 17 | 18 | // AddToScheme adds the types in this group-version to the given scheme. 19 | AddToScheme = SchemeBuilder.AddToScheme 20 | ) 21 | -------------------------------------------------------------------------------- /api/v1alpha3/sopssecret_types.go: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 | 5 | package v1alpha3 6 | 7 | import ( 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | ) 10 | 11 | // NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. 12 | // For upstream reference, see https://github.com/mozilla/sops/blob/master/stores/stores.go 13 | 14 | const ( 15 | // SopsSecretManagedAnnotation is the name for the annotation for 16 | // flagging the existing secret be managed by SopsSecret controller. 17 | SopsSecretManagedAnnotation = "sopssecret/managed" 18 | ) 19 | 20 | // SopsSecretSpec defines the desired state of SopsSecret 21 | type SopsSecretSpec struct { 22 | // Secrets template is a list of definitions to create Kubernetes Secrets 23 | //+kubebuilder:validation:MinItems=1 24 | //+required 25 | SecretsTemplate []SopsSecretTemplate `json:"secretTemplates"` 26 | 27 | // This flag tells the controller to suspend the reconciliation of this source. 28 | //+optional 29 | Suspend bool `json:"suspend,omitempty"` 30 | } 31 | 32 | // SopsSecretTemplate defines the map of secrets to create 33 | type SopsSecretTemplate struct { 34 | // Name of the Kubernetes secret to create 35 | //+required 36 | Name string `json:"name"` 37 | 38 | // Annotations to apply to Kubernetes secret 39 | //+optional 40 | Annotations map[string]string `json:"annotations,omitempty"` 41 | 42 | // Labels to apply to Kubernetes secret 43 | //+optional 44 | Labels map[string]string `json:"labels,omitempty"` 45 | 46 | // Kubernetes secret type. Default: Opauqe. Possible values: Opauqe, 47 | // kubernetes.io/service-account-token, kubernetes.io/dockercfg, 48 | // kubernetes.io/dockerconfigjson, kubernetes.io/basic-auth, 49 | // kubernetes.io/ssh-auth, kubernetes.io/tls, bootstrap.kubernetes.io/token 50 | //+optional 51 | Type string `json:"type,omitempty"` 52 | 53 | // Data map to use in Kubernetes secret (equivalent to Kubernetes Secret object data, please see for more 54 | // information: https://kubernetes.io/docs/concepts/configuration/secret/#overview-of-secrets) 55 | //+optional 56 | Data map[string]string `json:"data,omitempty"` 57 | 58 | // stringData map to use in Kubernetes secret (equivalent to Kubernetes Secret object stringData, please see for more 59 | // information: https://kubernetes.io/docs/concepts/configuration/secret/#overview-of-secrets) 60 | //+optional 61 | StringData map[string]string `json:"stringData,omitempty"` 62 | } 63 | 64 | // KmsDataItem defines AWS KMS specific encryption details 65 | type KmsDataItem struct { 66 | // Arn - KMS key ARN to use 67 | //+optional 68 | Arn string `json:"arn,omitempty"` 69 | // AWS Iam Role 70 | //+optional 71 | Role string `json:"role,omitempty"` 72 | 73 | //+optional 74 | EncryptedKey string `json:"enc,omitempty"` 75 | // Object creation date 76 | //+optional 77 | CreationDate string `json:"created_at,omitempty"` 78 | //+optional 79 | AwsProfile string `json:"aws_profile,omitempty"` 80 | } 81 | 82 | // PgpDataItem defines PGP specific encryption details 83 | type PgpDataItem struct { 84 | //+optional 85 | EncryptedKey string `json:"enc,omitempty"` 86 | 87 | // Object creation date 88 | //+optional 89 | CreationDate string `json:"created_at,omitempty"` 90 | // PGP FingerPrint of the key which can be used for decryption 91 | //+optional 92 | FingerPrint string `json:"fp,omitempty"` 93 | } 94 | 95 | // AzureKmsItem defines Azure Keyvault Key specific encryption details 96 | type AzureKmsItem struct { 97 | // Azure KMS vault URL 98 | //+optional 99 | VaultURL string `json:"vault_url,omitempty"` 100 | //+optional 101 | KeyName string `json:"name,omitempty"` 102 | //+optional 103 | Version string `json:"version,omitempty"` 104 | //+optional 105 | EncryptedKey string `json:"enc,omitempty"` 106 | // Object creation date 107 | //+optional 108 | CreationDate string `json:"created_at,omitempty"` 109 | } 110 | 111 | // AgeItem defines FiloSottile/age specific encryption details 112 | type AgeItem struct { 113 | // Recipient which private key can be used for decription 114 | //+optional 115 | Recipient string `json:"recipient,omitempty"` 116 | //+optional 117 | EncryptedKey string `json:"enc,omitempty"` 118 | } 119 | 120 | // HcVaultItem defines Hashicorp Vault Key specific encryption details 121 | type HcVaultItem struct { 122 | //+optional 123 | VaultAddress string `json:"vault_address,omitempty"` 124 | //+optional 125 | EnginePath string `json:"engine_path,omitempty"` 126 | //+optional 127 | KeyName string `json:"key_name,omitempty"` 128 | //+optional 129 | CreationDate string `json:"created_at,omitempty"` 130 | //+optional 131 | EncryptedKey string `json:"enc,omitempty"` 132 | } 133 | 134 | // GcpKmsDataItem defines GCP KMS Key specific encryption details 135 | type GcpKmsDataItem struct { 136 | //+optional 137 | VaultURL string `json:"resource_id,omitempty"` 138 | //+optional 139 | EncryptedKey string `json:"enc,omitempty"` 140 | // Object creation date 141 | //+optional 142 | CreationDate string `json:"created_at,omitempty"` 143 | } 144 | 145 | // SopsMetadata defines the encryption details 146 | type SopsMetadata struct { 147 | // Aws KMS configuration 148 | //+optional 149 | AwsKms []KmsDataItem `json:"kms,omitempty"` 150 | 151 | // PGP configuration 152 | //+optional 153 | Pgp []PgpDataItem `json:"pgp,omitempty"` 154 | 155 | // Azure KMS configuration 156 | //+optional 157 | AzureKms []AzureKmsItem `json:"azure_kv,omitempty"` 158 | 159 | // Hashicorp Vault KMS configurarion 160 | //+optional 161 | HcVault []HcVaultItem `json:"hc_vault,omitempty"` 162 | 163 | // Gcp KMS configuration 164 | //+optional 165 | GcpKms []GcpKmsDataItem `json:"gcp_kms,omitempty"` 166 | 167 | // Age configuration 168 | //+optional 169 | Age []AgeItem `json:"age,omitempty"` 170 | 171 | // Mac - sops setting 172 | //+optional 173 | Mac string `json:"mac,omitempty"` 174 | 175 | // LastModified date when SopsSecret was last modified 176 | //+optional 177 | LastModified string `json:"lastmodified,omitempty"` 178 | 179 | // Version of the sops tool used to encrypt SopsSecret 180 | //+optional 181 | Version string `json:"version,omitempty"` 182 | 183 | // Suffix used to encrypt SopsSecret resource 184 | //+optional 185 | EncryptedSuffix string `json:"encrypted_suffix,omitempty"` 186 | 187 | // Regex used to encrypt SopsSecret resource 188 | // This opstion should be used with more care, as it can make resource unapplicable to the cluster. 189 | //+optional 190 | EncryptedRegex string `json:"encrypted_regex,omitempty"` 191 | } 192 | 193 | // SopsSecretStatus defines the observed state of SopsSecret 194 | type SopsSecretStatus struct { 195 | // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster 196 | // Important: Run "make" to regenerate code after modifying this file 197 | 198 | // SopsSecret status message 199 | //+optional 200 | Message string `json:"message,omitempty"` 201 | } 202 | 203 | //+kubebuilder:object:root=true 204 | //+kubebuilder:subresource:status 205 | 206 | // SopsSecret is the Schema for the sopssecrets API 207 | // +kubebuilder:resource:shortName=sops,scope=Namespaced 208 | // +kubebuilder:subresource:status 209 | // +kubebuilder:storageversion 210 | // +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.message` 211 | type SopsSecret struct { 212 | metav1.TypeMeta `json:",inline"` 213 | metav1.ObjectMeta `json:"metadata,omitempty"` 214 | 215 | // SopsSecret Spec definition 216 | Spec SopsSecretSpec `json:"spec,omitempty"` 217 | // SopsSecret Status information 218 | Status SopsSecretStatus `json:"status,omitempty"` 219 | // SopsSecret metadata 220 | Sops SopsMetadata `json:"sops,omitempty"` 221 | } 222 | 223 | //+kubebuilder:object:root=true 224 | 225 | // SopsSecretList contains a list of SopsSecret 226 | type SopsSecretList struct { 227 | metav1.TypeMeta `json:",inline"` 228 | metav1.ListMeta `json:"metadata,omitempty"` 229 | Items []SopsSecret `json:"items"` 230 | } 231 | 232 | func init() { 233 | SchemeBuilder.Register(&SopsSecret{}, &SopsSecretList{}) 234 | } 235 | -------------------------------------------------------------------------------- /api/v1alpha3/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | 3 | /* This Source Code Form is subject to the terms of the Mozilla Public 4 | * License, v. 2.0. If a copy of the MPL was not distributed with this 5 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 6 | 7 | // Code generated by controller-gen. DO NOT EDIT. 8 | 9 | package v1alpha3 10 | 11 | import ( 12 | runtime "k8s.io/apimachinery/pkg/runtime" 13 | ) 14 | 15 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 16 | func (in *AgeItem) DeepCopyInto(out *AgeItem) { 17 | *out = *in 18 | } 19 | 20 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AgeItem. 21 | func (in *AgeItem) DeepCopy() *AgeItem { 22 | if in == nil { 23 | return nil 24 | } 25 | out := new(AgeItem) 26 | in.DeepCopyInto(out) 27 | return out 28 | } 29 | 30 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 31 | func (in *AzureKmsItem) DeepCopyInto(out *AzureKmsItem) { 32 | *out = *in 33 | } 34 | 35 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureKmsItem. 36 | func (in *AzureKmsItem) DeepCopy() *AzureKmsItem { 37 | if in == nil { 38 | return nil 39 | } 40 | out := new(AzureKmsItem) 41 | in.DeepCopyInto(out) 42 | return out 43 | } 44 | 45 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 46 | func (in *GcpKmsDataItem) DeepCopyInto(out *GcpKmsDataItem) { 47 | *out = *in 48 | } 49 | 50 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GcpKmsDataItem. 51 | func (in *GcpKmsDataItem) DeepCopy() *GcpKmsDataItem { 52 | if in == nil { 53 | return nil 54 | } 55 | out := new(GcpKmsDataItem) 56 | in.DeepCopyInto(out) 57 | return out 58 | } 59 | 60 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 61 | func (in *HcVaultItem) DeepCopyInto(out *HcVaultItem) { 62 | *out = *in 63 | } 64 | 65 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HcVaultItem. 66 | func (in *HcVaultItem) DeepCopy() *HcVaultItem { 67 | if in == nil { 68 | return nil 69 | } 70 | out := new(HcVaultItem) 71 | in.DeepCopyInto(out) 72 | return out 73 | } 74 | 75 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 76 | func (in *KmsDataItem) DeepCopyInto(out *KmsDataItem) { 77 | *out = *in 78 | } 79 | 80 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KmsDataItem. 81 | func (in *KmsDataItem) DeepCopy() *KmsDataItem { 82 | if in == nil { 83 | return nil 84 | } 85 | out := new(KmsDataItem) 86 | in.DeepCopyInto(out) 87 | return out 88 | } 89 | 90 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 91 | func (in *PgpDataItem) DeepCopyInto(out *PgpDataItem) { 92 | *out = *in 93 | } 94 | 95 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PgpDataItem. 96 | func (in *PgpDataItem) DeepCopy() *PgpDataItem { 97 | if in == nil { 98 | return nil 99 | } 100 | out := new(PgpDataItem) 101 | in.DeepCopyInto(out) 102 | return out 103 | } 104 | 105 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 106 | func (in *SopsMetadata) DeepCopyInto(out *SopsMetadata) { 107 | *out = *in 108 | if in.AwsKms != nil { 109 | in, out := &in.AwsKms, &out.AwsKms 110 | *out = make([]KmsDataItem, len(*in)) 111 | copy(*out, *in) 112 | } 113 | if in.Pgp != nil { 114 | in, out := &in.Pgp, &out.Pgp 115 | *out = make([]PgpDataItem, len(*in)) 116 | copy(*out, *in) 117 | } 118 | if in.AzureKms != nil { 119 | in, out := &in.AzureKms, &out.AzureKms 120 | *out = make([]AzureKmsItem, len(*in)) 121 | copy(*out, *in) 122 | } 123 | if in.HcVault != nil { 124 | in, out := &in.HcVault, &out.HcVault 125 | *out = make([]HcVaultItem, len(*in)) 126 | copy(*out, *in) 127 | } 128 | if in.GcpKms != nil { 129 | in, out := &in.GcpKms, &out.GcpKms 130 | *out = make([]GcpKmsDataItem, len(*in)) 131 | copy(*out, *in) 132 | } 133 | if in.Age != nil { 134 | in, out := &in.Age, &out.Age 135 | *out = make([]AgeItem, len(*in)) 136 | copy(*out, *in) 137 | } 138 | } 139 | 140 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsMetadata. 141 | func (in *SopsMetadata) DeepCopy() *SopsMetadata { 142 | if in == nil { 143 | return nil 144 | } 145 | out := new(SopsMetadata) 146 | in.DeepCopyInto(out) 147 | return out 148 | } 149 | 150 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 151 | func (in *SopsSecret) DeepCopyInto(out *SopsSecret) { 152 | *out = *in 153 | out.TypeMeta = in.TypeMeta 154 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 155 | in.Spec.DeepCopyInto(&out.Spec) 156 | out.Status = in.Status 157 | in.Sops.DeepCopyInto(&out.Sops) 158 | } 159 | 160 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsSecret. 161 | func (in *SopsSecret) DeepCopy() *SopsSecret { 162 | if in == nil { 163 | return nil 164 | } 165 | out := new(SopsSecret) 166 | in.DeepCopyInto(out) 167 | return out 168 | } 169 | 170 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 171 | func (in *SopsSecret) DeepCopyObject() runtime.Object { 172 | if c := in.DeepCopy(); c != nil { 173 | return c 174 | } 175 | return nil 176 | } 177 | 178 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 179 | func (in *SopsSecretList) DeepCopyInto(out *SopsSecretList) { 180 | *out = *in 181 | out.TypeMeta = in.TypeMeta 182 | in.ListMeta.DeepCopyInto(&out.ListMeta) 183 | if in.Items != nil { 184 | in, out := &in.Items, &out.Items 185 | *out = make([]SopsSecret, len(*in)) 186 | for i := range *in { 187 | (*in)[i].DeepCopyInto(&(*out)[i]) 188 | } 189 | } 190 | } 191 | 192 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsSecretList. 193 | func (in *SopsSecretList) DeepCopy() *SopsSecretList { 194 | if in == nil { 195 | return nil 196 | } 197 | out := new(SopsSecretList) 198 | in.DeepCopyInto(out) 199 | return out 200 | } 201 | 202 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 203 | func (in *SopsSecretList) DeepCopyObject() runtime.Object { 204 | if c := in.DeepCopy(); c != nil { 205 | return c 206 | } 207 | return nil 208 | } 209 | 210 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 211 | func (in *SopsSecretSpec) DeepCopyInto(out *SopsSecretSpec) { 212 | *out = *in 213 | if in.SecretsTemplate != nil { 214 | in, out := &in.SecretsTemplate, &out.SecretsTemplate 215 | *out = make([]SopsSecretTemplate, len(*in)) 216 | for i := range *in { 217 | (*in)[i].DeepCopyInto(&(*out)[i]) 218 | } 219 | } 220 | } 221 | 222 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsSecretSpec. 223 | func (in *SopsSecretSpec) DeepCopy() *SopsSecretSpec { 224 | if in == nil { 225 | return nil 226 | } 227 | out := new(SopsSecretSpec) 228 | in.DeepCopyInto(out) 229 | return out 230 | } 231 | 232 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 233 | func (in *SopsSecretStatus) DeepCopyInto(out *SopsSecretStatus) { 234 | *out = *in 235 | } 236 | 237 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsSecretStatus. 238 | func (in *SopsSecretStatus) DeepCopy() *SopsSecretStatus { 239 | if in == nil { 240 | return nil 241 | } 242 | out := new(SopsSecretStatus) 243 | in.DeepCopyInto(out) 244 | return out 245 | } 246 | 247 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 248 | func (in *SopsSecretTemplate) DeepCopyInto(out *SopsSecretTemplate) { 249 | *out = *in 250 | if in.Annotations != nil { 251 | in, out := &in.Annotations, &out.Annotations 252 | *out = make(map[string]string, len(*in)) 253 | for key, val := range *in { 254 | (*out)[key] = val 255 | } 256 | } 257 | if in.Labels != nil { 258 | in, out := &in.Labels, &out.Labels 259 | *out = make(map[string]string, len(*in)) 260 | for key, val := range *in { 261 | (*out)[key] = val 262 | } 263 | } 264 | if in.Data != nil { 265 | in, out := &in.Data, &out.Data 266 | *out = make(map[string]string, len(*in)) 267 | for key, val := range *in { 268 | (*out)[key] = val 269 | } 270 | } 271 | if in.StringData != nil { 272 | in, out := &in.StringData, &out.StringData 273 | *out = make(map[string]string, len(*in)) 274 | for key, val := range *in { 275 | (*out)[key] = val 276 | } 277 | } 278 | } 279 | 280 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SopsSecretTemplate. 281 | func (in *SopsSecretTemplate) DeepCopy() *SopsSecretTemplate { 282 | if in == nil { 283 | return nil 284 | } 285 | out := new(SopsSecretTemplate) 286 | in.DeepCopyInto(out) 287 | return out 288 | } 289 | -------------------------------------------------------------------------------- /chart/crd: -------------------------------------------------------------------------------- 1 | ../config/crd -------------------------------------------------------------------------------- /chart/helm3/README.md.gotmpl: -------------------------------------------------------------------------------- 1 | {{ template "chart.header" . }} 2 | 3 | {{ template "chart.description" . }} 4 | 5 | {{ template "chart.deprecationWarning" . }} 6 | 7 | {{ template "chart.homepageLine" . }} 8 | 9 | {{ template "chart.sourcesSection" . }} 10 | 11 | ## TL;DR; 12 | 13 | ```console 14 | $ kubectl create namespace sops 15 | 16 | $ kubectl apply -f deploy/crds/isindir_v1alpha1_sopssecret_crd.yaml 17 | 18 | $ helm repo add sops https://isindir.github.io/sops-secrets-operator/ 19 | 20 | $ helm upgrade --install sops sops/sops-secrets-operator \ 21 | --namespace sops -f custom.values.yaml 22 | ``` 23 | 24 | > where `custom.values.yaml` must customise deployment and configure access to Cloud KMS 25 | 26 | * AWS is supported via `kiam` namespace and pod annotations or via [IAM roles for service accounts](https://docs.aws.amazon.com/eks/latest/userguide/specify-service-account-role.html) 27 | * GCP is supported via service account secret which allows decryption using GCP KMS 28 | * GPG is supported via secrets with GPG configuration 29 | * Azure is supported via a Service principal plus a secret 30 | * Vault is supported via vault agent injections 31 | * Age is supported via mounting secret and defining environment variable 32 | 33 | ## Introduction 34 | 35 | This chart bootstraps a [sops-secrets-operator](https://github.com/isindir/sops-secrets-operator.git) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. 36 | 37 | ## Installing the Chart 38 | 39 | ### AWS 40 | 41 | * Deploy [kiam](https://github.com/uswitch/kiam) using [kiam chart](https://github.com/helm/charts/tree/master/stable/kiam) 42 | * Alternatively [kube2iam](https://github.com/jtblin/kube2iam) 43 | * Or provide credentials to perform AWS KMS operations 44 | * Create IAM assume role which allows to use KMS key for decryption 45 | * Create Kubernetes namespace for operator deployment, with kiam annotation 46 | * Apply `sops-secrets-operator` CRD 47 | * Deploy helm chart 48 | 49 | ### GCP 50 | 51 | * Create GCP Service Account which allows to use KMS to decrypt 52 | * Either put the GCP Service Account JSON file in your custom values.yaml file or create a Kubernetes Secret with the same information and put the name of that secret in your values.yaml. Enable GCP in the Helm Chart by setting `gcp.enabled: true` in values.yaml. 53 | 54 | * Create custom values file in a following format: 55 | 56 | ```yaml 57 | gcp: 58 | enabled: true 59 | svcAccSecret: |- 60 | { 61 | "type": "service_account", 62 | ... 63 | } 64 | ``` 65 | 66 | or 67 | 68 | ```yaml 69 | gcp: 70 | enabled: true 71 | existingSecretName: gcp-sa-existing-secret-name 72 | ``` 73 | 74 | * Create Kubernetes namespace for operator deployment 75 | * Apply `sops-secrets-operator` CRD 76 | * Deploy helm chart specifying extra values file 77 | 78 | ### Azure 79 | 80 | * Create a KeyVault if you don't have one already 81 | * Create a Key in that KeyVault 82 | * Create Service principal with permissions to use the key for Encryption/Decryption 83 | * follow the [SOPS documentation](https://github.com/mozilla/sops#encrypting-using-azure-key-vault) 84 | * Either put Tenant ID, Client ID and Client Secret for the Service Principal in your custom values.yaml file or create a Kubernetes Secret with the same information and put the name of that secret in your values.yaml. Enable Azure in the Helm Chart by setting `azure.enabled: true` in values.yaml. 85 | 86 | ### Age 87 | 88 | * Create age `keys.txt` file 89 | * Create Kubernetes secret using `keys.txt` 90 | * When deploying helm chart use `extraEnv` value to speicify environment variable `SOPS_AGE_RECIPIENTS` and `secretsAsFiles` value to mount `keys.txt` 91 | 92 | For reference see: 93 | 94 | * [Age encryption tool](https://github.com/FiloSottile/age) 95 | * [sops section on how to encrypt](https://github.com/mozilla/sops#22encrypting-using-age) 96 | * Also see: [Local testing using age](docs/age/README.md) 97 | 98 | ## Uninstalling the Chart 99 | 100 | To uninstall/delete the `my-release` deployment: 101 | 102 | ```console 103 | $ helm uninstall sops 104 | ``` 105 | 106 | The command removes all the Kubernetes components associated with the chart and deletes the release. 107 | 108 | ## Configuration 109 | 110 | The following table lists the configurable parameters of the Sops-secrets-operator chart and their default values. 111 | 112 | {{ template "chart.requirementsSection" . }} 113 | 114 | {{ template "chart.valuesSection" . }} 115 | 116 | Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, 117 | 118 | Alternatively, a YAML file that specifies the values for the above parameters can be provided while installing the chart. For example, 119 | 120 | > **Tip**: You can use the default [values.yaml](values.yaml) 121 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/.helmignore: -------------------------------------------------------------------------------- 1 | # Patterns to ignore when building packages. 2 | # This supports shell glob matching, relative path matching, and 3 | # negation (prefixed with !). Only one pattern per line. 4 | .DS_Store 5 | # Common VCS dirs 6 | .git/ 7 | .gitignore 8 | .bzr/ 9 | .bzrignore 10 | .hg/ 11 | .hgignore 12 | .svn/ 13 | # Common backup files 14 | *.swp 15 | *.bak 16 | *.tmp 17 | *~ 18 | # Various IDEs 19 | .project 20 | .idea/ 21 | *.tmproj 22 | .vscode/ 23 | # Custom 24 | qqq.* 25 | abc.* 26 | *.qqq 27 | *.abc 28 | *.vim 29 | Makefile 30 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/Chart.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v2 2 | # UPDATE_HERE 3 | version: 0.22.0 4 | appVersion: 0.16.0 5 | type: application 6 | description: Helm chart deploys sops-secrets-operator 7 | name: sops-secrets-operator 8 | sources: 9 | - https://github.com/isindir/sops-secrets-operator.git 10 | maintainers: 11 | - name: isindir 12 | email: isindir@users.sf.net 13 | metadata: 14 | annotations: 15 | artifacthub.io/operator: "true" 16 | artifacthub.io/links: 17 | - name: "SOPS: Secrets OPerationS - Kubernetes Operator github project" 18 | url: "https://github.com/isindir/sops-secrets-operator.git" 19 | - name: "SOPS: Secrets OPerationS" 20 | url: "https://github.com/mozilla/sops" 21 | artifacthub.io/maintainers: 22 | - name: isindir 23 | email: isindir@users.sf.net 24 | artifacthub.io/operatorCapabilities: "Full Lifecycle" 25 | artifacthub.io/crds: 26 | - kind: SopsSecret 27 | version: isindir.github.com/v1alpha3 28 | name: sopssecret 29 | displayName: SopsSecret 30 | description: SopsSecret - encapsulates sops encrypted kubernetes secrets definitions 31 | keywords: 32 | - gitops 33 | - sops 34 | - kms 35 | - encryption 36 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all dep list test 2 | 3 | CHART_NAME?=$(shell cat Chart.yaml | awk 'BEGIN { FS=": " } $$0~/^name:/ { gsub(/['\'',]/, ""); print $$2; }') 4 | VERSION_TAG?=$(shell cat Chart.yaml | awk 'BEGIN { FS=": " } $$0~/^version/ { gsub(/['\'',]/, ""); print $$2; }') 5 | 6 | # UPDATE_HERE 7 | K8S_VERSION := "1.33.0" 8 | 9 | SHELL=/bin/bash 10 | 11 | ##@ General 12 | 13 | .PHONY: all 14 | all: echo lint test validate ## run all test targets 15 | 16 | .PHONY: help 17 | help: ## Display this help. 18 | @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) 19 | 20 | .PHONY: versions 21 | versions: ## shows currently installed tool versions 22 | helm version ; echo 23 | helm plugin list | grep unittest ; echo 24 | @echo '--------------------' 25 | @asdf current 2>/dev/null | grep sops-secrets-operator 26 | @echo '--------------------' 27 | 28 | .PHONY: echo 29 | echo: ## prints chart information 30 | @echo '-=-=-=-=-=-=-=-=-=-=- "${CHART_NAME}" version: "${VERSION_TAG}" -=-=-=-=-=-=-=-=-=-=-' 31 | 32 | .PHONY: test 33 | test: ## runs unittests 34 | helm unittest --color . 35 | @echo '--------------------' 36 | 37 | .PHONY: lint 38 | lint: ## runs helm chart linting 39 | helm lint . 40 | @echo '--------------------' 41 | 42 | .PHONY: validate 43 | validate: ## validates rendered chart templates using 'kubeconform' 44 | helm template . \ 45 | --set securityContextenabled=true \ 46 | --set metrics.enabled=true \ 47 | --set gcp.enabled=true \ 48 | --set azure.enabled=true \ 49 | | kubeconform -summary \ 50 | -verbose \ 51 | -strict \ 52 | -output pretty \ 53 | -schema-location https://raw.githubusercontent.com/Onemind-Services-LLC/kubernetes-json-schema/master/schema \ 54 | -schema-location https://raw.githubusercontent.com/datreeio/CRDs-catalog/main/monitoring.coreos.com/servicemonitor_v1.json \ 55 | -kubernetes-version $(K8S_VERSION) - 56 | @echo '--------------------' 57 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/crds/isindir.github.com_sopssecrets.yaml: -------------------------------------------------------------------------------- 1 | ../../../../config/crd/bases/isindir.github.com_sopssecrets.yaml -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/templates/NOTES.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/chart/helm3/sops-secrets-operator/templates/NOTES.txt -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/templates/_helpers.tpl: -------------------------------------------------------------------------------- 1 | {{/* vim: set filetype=mustache: */}} 2 | {{/* 3 | Expand the name of the chart. 4 | */}} 5 | {{- define "sops-secrets-operator.name" -}} 6 | {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} 7 | {{- end -}} 8 | 9 | {{/* 10 | Create a default fully qualified app name. 11 | We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). 12 | If release name contains chart name it will be used as a full name. 13 | */}} 14 | {{- define "sops-secrets-operator.fullname" -}} 15 | {{- if .Values.fullnameOverride -}} 16 | {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} 17 | {{- else -}} 18 | {{- $name := default .Chart.Name .Values.nameOverride -}} 19 | {{- if contains $name .Release.Name -}} 20 | {{- .Release.Name | trunc 63 | trimSuffix "-" -}} 21 | {{- else -}} 22 | {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} 23 | {{- end -}} 24 | {{- end -}} 25 | {{- end -}} 26 | 27 | {{/* 28 | Create chart name and version as used by the chart label. 29 | */}} 30 | {{- define "sops-secrets-operator.chart" -}} 31 | {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} 32 | {{- end -}} 33 | 34 | {{/* 35 | Common labels 36 | */}} 37 | {{- define "sops-secrets-operator.labels" -}} 38 | app.kubernetes.io/name: {{ include "sops-secrets-operator.name" . }} 39 | helm.sh/chart: {{ include "sops-secrets-operator.chart" . }} 40 | app.kubernetes.io/instance: {{ .Release.Name }} 41 | {{- if .Chart.AppVersion }} 42 | app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} 43 | {{- end }} 44 | app.kubernetes.io/managed-by: {{ .Release.Service }} 45 | {{- end -}} 46 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/templates/azure_secret.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.azure.enabled (not .Values.azure.existingSecretName) }} 2 | kind: Secret 3 | apiVersion: v1 4 | metadata: 5 | name: {{ include "sops-secrets-operator.name" . }}-azure-secret 6 | labels: 7 | {{ include "sops-secrets-operator.labels" . | indent 4 }} 8 | type: Opaque 9 | stringData: 10 | tenantId: {{ .Values.azure.tenantId }} 11 | clientId: {{ .Values.azure.clientId }} 12 | clientSecret: {{ .Values.azure.clientSecret }} 13 | {{- end }} 14 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/templates/cluster_role.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.rbac.enabled }} 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | {{- if .Values.namespaced }} 4 | kind: Role 5 | {{- else }} 6 | kind: ClusterRole 7 | {{- end }} 8 | metadata: 9 | name: {{ include "sops-secrets-operator.fullname" . }} 10 | labels: 11 | {{ include "sops-secrets-operator.labels" . | indent 4 }} 12 | rules: 13 | - apiGroups: 14 | - coordination.k8s.io 15 | resources: 16 | - leases 17 | verbs: 18 | - '*' 19 | - apiGroups: 20 | - "" 21 | resources: 22 | - configmaps 23 | - secrets 24 | verbs: 25 | - '*' 26 | - apiGroups: 27 | - "" 28 | resources: 29 | - secrets/status 30 | verbs: 31 | - get 32 | - patch 33 | - update 34 | - apiGroups: 35 | - events.k8s.io 36 | - "" 37 | resources: 38 | - events 39 | verbs: 40 | - '*' 41 | - apiGroups: 42 | - monitoring.coreos.com 43 | resources: 44 | - servicemonitors 45 | verbs: 46 | - get 47 | - create 48 | - apiGroups: 49 | - isindir.github.com 50 | resources: 51 | - sopssecrets 52 | verbs: 53 | - create 54 | - delete 55 | - get 56 | - list 57 | - patch 58 | - update 59 | - watch 60 | - apiGroups: 61 | - isindir.github.com 62 | resources: 63 | - sopssecrets/finalizers 64 | verbs: 65 | - update 66 | - apiGroups: 67 | - isindir.github.com 68 | resources: 69 | - sopssecrets/status 70 | verbs: 71 | - get 72 | - patch 73 | - update 74 | {{- end }} 75 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/templates/cluster_role_binding.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.rbac.enabled }} 2 | {{- if .Values.namespaced }} 3 | kind: RoleBinding 4 | {{- else }} 5 | kind: ClusterRoleBinding 6 | {{- end }} 7 | apiVersion: rbac.authorization.k8s.io/v1 8 | metadata: 9 | name: {{ include "sops-secrets-operator.fullname" . }} 10 | labels: 11 | {{ include "sops-secrets-operator.labels" . | indent 4 }} 12 | subjects: 13 | - kind: ServiceAccount 14 | name: {{ .Values.serviceAccount.name | default (tpl ( include "sops-secrets-operator.fullname" . ) .) }} 15 | namespace: {{ .Release.Namespace }} 16 | roleRef: 17 | {{- if .Values.namespaced }} 18 | kind: Role 19 | {{- else }} 20 | kind: ClusterRole 21 | {{- end }} 22 | name: {{ include "sops-secrets-operator.fullname" . }} 23 | apiGroup: rbac.authorization.k8s.io 24 | {{- end }} 25 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/templates/gcp_secret.yaml: -------------------------------------------------------------------------------- 1 | {{- if and .Values.gcp.enabled (not .Values.gcp.existingSecretName) }} 2 | kind: Secret 3 | apiVersion: v1 4 | metadata: 5 | {{- if .Values.gcp.svcAccSecretCustomName }} 6 | name: {{ .Values.gcp.svcAccSecretCustomName }} 7 | {{- else }} 8 | name: {{ include "sops-secrets-operator.name" . }}-gcp-secret 9 | {{- end }} 10 | labels: 11 | {{ include "sops-secrets-operator.labels" . | indent 4 }} 12 | type: Opaque 13 | stringData: 14 | key.json: |- 15 | {{ .Values.gcp.svcAccSecret | indent 4 }} 16 | {{- end }} 17 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/templates/monitor.yaml: -------------------------------------------------------------------------------- 1 | {{- if .Values.metrics.enabled }} 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: ServiceMonitor 4 | metadata: 5 | name: {{ include "sops-secrets-operator.fullname" . }}-metrics-monitor 6 | labels: 7 | {{- include "sops-secrets-operator.labels" . | nindent 4 }} 8 | {{- with .Values.metrics.additionalLabels }} 9 | {{- toYaml . | nindent 4 }} 10 | {{- end }} 11 | spec: 12 | endpoints: 13 | - path: /metrics 14 | port: https 15 | scheme: https 16 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 17 | tlsConfig: 18 | insecureSkipVerify: true 19 | selector: 20 | matchLabels: 21 | app.kubernetes.io/name: {{ include "sops-secrets-operator.name" . }} 22 | app.kubernetes.io/instance: {{ .Release.Name }} 23 | {{- end }} 24 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/templates/operator.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: {{ include "sops-secrets-operator.fullname" . }} 5 | labels: 6 | {{ include "sops-secrets-operator.labels" . | indent 4 }} 7 | spec: 8 | replicas: {{ .Values.replicaCount }} 9 | selector: 10 | matchLabels: 11 | app.kubernetes.io/name: {{ include "sops-secrets-operator.name" . }} 12 | app.kubernetes.io/instance: {{ .Release.Name }} 13 | template: 14 | metadata: 15 | annotations: 16 | kubectl.kubernetes.io/default-container: {{ .Chart.Name }} 17 | {{- if .Values.podAnnotations }} 18 | {{ toYaml .Values.podAnnotations | nindent 8 }} 19 | {{- end }} 20 | labels: 21 | control-plane: controller-{{ .Chart.Name }} 22 | app.kubernetes.io/name: {{ include "sops-secrets-operator.name" . }} 23 | app.kubernetes.io/instance: {{ .Release.Name }} 24 | {{- if .Values.podLabels }} 25 | {{ toYaml .Values.podLabels | nindent 8 }} 26 | {{- end }} 27 | spec: 28 | {{- with .Values.imagePullSecrets }} 29 | imagePullSecrets: 30 | {{- toYaml . | nindent 8 }} 31 | {{- end }} 32 | serviceAccountName: {{ .Values.serviceAccount.name | default (tpl ( include "sops-secrets-operator.fullname" . ) .) }} 33 | {{- if .Values.gpg.enabled }} 34 | initContainers: 35 | - name: init-myservice 36 | # https://hub.docker.com/_/ubuntu?tab=tags&page=1&ordering=last_updated 37 | image: "{{ .Values.initImage.repository }}:{{ .Values.initImage.tag }}" 38 | imagePullPolicy: {{ .Values.initImage.pullPolicy }} 39 | command: ['/bin/sh', '-c', 'cp -Lr /var/secrets/gpg-secrets/* /var/secrets/gpg/'] 40 | {{- if and .Values.securityContext.enabled .Values.securityContext.container.enabled }} 41 | securityContext: 42 | capabilities: 43 | drop: {{ .Values.securityContext.container.capabilities.drop }} 44 | add: {{ .Values.securityContext.container.capabilities.add }} 45 | {{- end }} 46 | volumeMounts: 47 | - mountPath: /var/secrets/gpg 48 | name: sops-gpg 49 | - mountPath: /var/secrets/gpg-secrets 50 | name: sops-operator-gpg-keys1 51 | - mountPath: /var/secrets/gpg-secrets/private-keys-v1.d 52 | name: sops-operator-gpg-keys2 53 | {{- end }} 54 | containers: 55 | - name: {{ .Chart.Name }} 56 | image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}" 57 | imagePullPolicy: {{ .Values.image.pullPolicy }} 58 | {{- if and .Values.securityContext.enabled .Values.securityContext.container.enabled }} 59 | securityContext: 60 | capabilities: 61 | drop: {{ .Values.securityContext.container.capabilities.drop }} 62 | add: {{ .Values.securityContext.container.capabilities.add }} 63 | {{- end }} 64 | {{- if or .Values.gcp.enabled .Values.gpg.enabled .Values.secretsAsFiles }} 65 | volumeMounts: 66 | {{- end }} 67 | {{- if .Values.gcp.enabled }} 68 | - mountPath: /var/secrets/google 69 | name: sops-operator-gke-svc-account 70 | {{- end }} 71 | {{- if .Values.gpg.enabled }} 72 | - mountPath: /var/secrets/gpg 73 | name: sops-gpg 74 | - mountPath: /var/secrets/gpg-secrets 75 | name: sops-operator-gpg-keys1 76 | - mountPath: /var/secrets/gpg-secrets/private-keys-v1.d 77 | name: sops-operator-gpg-keys2 78 | {{- end }} 79 | {{- range .Values.secretsAsFiles }} 80 | - name: {{ .name }} 81 | mountPath: {{ .mountPath }} 82 | readOnly: true 83 | {{- end }} 84 | command: 85 | - /usr/local/bin/manager 86 | args: 87 | # The address the metric endpoint binds to. (default ":8080") 88 | #- "-metrics-bind-address=127.0.0.1:8080" 89 | - "-health-probe-bind-address=:{{ .Values.healthProbes.port }}" 90 | # Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager. 91 | - "-leader-elect" 92 | - "-requeue-decrypt-after={{ .Values.requeueAfter }}" 93 | - "-zap-devel={{ .Values.logging.development }}" 94 | - "-zap-encoder={{ .Values.logging.encoder }}" 95 | - "-zap-log-level={{ .Values.logging.level }}" 96 | - "-zap-stacktrace-level={{ .Values.logging.stacktraceLevel }}" 97 | - "-zap-time-encoding={{ .Values.logging.timeEncoding }}" 98 | {{- if .Values.namespaced }} 99 | - "-watch-namespace={{ .Release.Namespace }}" 100 | {{- end -}} 101 | {{- if .Values.kubeconfig.enabled }} 102 | - "-kubeconfig={{ .Values.kubeconfig.path | quote }}" 103 | {{- end }} 104 | livenessProbe: 105 | httpGet: 106 | path: /healthz 107 | port: {{ .Values.healthProbes.port }} 108 | initialDelaySeconds: {{ .Values.healthProbes.liveness.initialDelaySeconds }} 109 | periodSeconds: {{ .Values.healthProbes.liveness.periodSeconds }} 110 | readinessProbe: 111 | httpGet: 112 | path: /readyz 113 | port: {{ .Values.healthProbes.port }} 114 | initialDelaySeconds: {{ .Values.healthProbes.readiness.initialDelaySeconds }} 115 | periodSeconds: {{ .Values.healthProbes.readiness.periodSeconds }} 116 | env: 117 | - name: POD_NAME 118 | valueFrom: 119 | fieldRef: 120 | fieldPath: metadata.name 121 | {{- if .Values.gcp.enabled }} 122 | - name: GOOGLE_APPLICATION_CREDENTIALS 123 | value: /var/secrets/google/key.json 124 | {{- end }} 125 | {{- if .Values.gpg.enabled }} 126 | - name: GNUPGHOME 127 | value: /var/secrets/gpg 128 | {{- end }} 129 | {{- if .Values.azure.enabled }} 130 | {{- $secretname := printf "%s-azure-secret" (include "sops-secrets-operator.name" .) -}} 131 | {{- if .Values.azure.existingSecretName }} 132 | {{- $secretname = .Values.azure.existingSecretName -}} 133 | {{- end }} 134 | - name: AZURE_TENANT_ID 135 | valueFrom: 136 | secretKeyRef: 137 | name: {{ $secretname }} 138 | key: tenantId 139 | - name: AZURE_CLIENT_ID 140 | valueFrom: 141 | secretKeyRef: 142 | name: {{ $secretname }} 143 | key: clientId 144 | - name: AZURE_CLIENT_SECRET 145 | valueFrom: 146 | secretKeyRef: 147 | name: {{ $secretname }} 148 | key: clientSecret 149 | {{- end }} 150 | {{- range .Values.secretsAsEnvVars }} 151 | - name: {{ .name }} 152 | valueFrom: 153 | secretKeyRef: 154 | name: {{ .secretName }} 155 | key: {{ .secretKey }} 156 | {{- end }} 157 | {{- range .Values.extraEnv }} 158 | - name: {{ .name }} 159 | value: {{ .value | quote }} 160 | {{- end }} 161 | resources: 162 | {{- toYaml .Values.resources | nindent 12 }} 163 | {{- if or .Values.gcp.enabled .Values.gpg.enabled .Values.secretsAsFiles }} 164 | volumes: 165 | {{- end }} 166 | {{- if .Values.gcp.enabled }} 167 | - name: sops-operator-gke-svc-account 168 | secret: 169 | {{- if .Values.gcp.existingSecretName }} 170 | secretName: {{ .Values.gcp.existingSecretName }} 171 | {{- else if .Values.gcp.svcAccSecretCustomName }} 172 | secretName: {{ .Values.gcp.svcAccSecretCustomName }} 173 | {{- else }} 174 | secretName: {{ include "sops-secrets-operator.name" . }}-gcp-secret 175 | {{- end }} 176 | {{- end }} 177 | {{- if .Values.gpg.enabled }} 178 | - name: sops-operator-gpg-keys1 179 | secret: 180 | secretName: {{ .Values.gpg.secret1 }} 181 | - name: sops-operator-gpg-keys2 182 | secret: 183 | secretName: {{ .Values.gpg.secret2 }} 184 | - name: sops-gpg 185 | emptyDir: {} 186 | {{- end }} 187 | {{- range .Values.secretsAsFiles }} 188 | - name: {{ .name }} 189 | secret: 190 | secretName: {{ .secretName }} 191 | {{- end }} 192 | {{- with .Values.nodeSelector }} 193 | nodeSelector: 194 | {{- toYaml . | nindent 8 }} 195 | {{- end }} 196 | {{- if .Values.securityContext.enabled }} 197 | securityContext: 198 | runAsUser: {{ .Values.securityContext.runAsUser }} 199 | runAsGroup: {{ .Values.securityContext.runAsGroup }} 200 | fsGroup: {{ .Values.securityContext.fsGroup }} 201 | runAsNonRoot: {{ .Values.securityContext.runAsNonRoot }} 202 | seccompProfile: 203 | type: {{ .Values.securityContext.seccompProfileType }} 204 | {{- if eq .Values.securityContext.seccompProfileType "Localhost" }} 205 | localhostProfile: {{ .Values.securityContext.seccompProfileName }} 206 | {{- end }} 207 | {{- end }} 208 | {{- with .Values.affinity }} 209 | affinity: 210 | {{- toYaml . | nindent 8 }} 211 | {{- end }} 212 | {{- with .Values.tolerations }} 213 | tolerations: 214 | {{- toYaml . | nindent 8 }} 215 | {{- end }} 216 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/templates/service_account.yaml: -------------------------------------------------------------------------------- 1 | {{- if (and .Values.rbac.enabled .Values.serviceAccount.enabled) }} 2 | apiVersion: v1 3 | kind: ServiceAccount 4 | metadata: 5 | {{- with .Values.serviceAccount.annotations }} 6 | annotations: 7 | {{ toYaml . | indent 4 }} 8 | {{- end }} 9 | name: {{ .Values.serviceAccount.name | default (tpl ( include "sops-secrets-operator.fullname" . ) .) }} 10 | labels: 11 | {{ include "sops-secrets-operator.labels" . | indent 4 }} 12 | {{- end }} 13 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/templates/validation.yaml: -------------------------------------------------------------------------------- 1 | {{- if and (not .Values.serviceAccount.enabled) (not .Values.serviceAccount.name) }} 2 | {{- fail "Error: serviceAccount 'name' must be set if serviceAccount 'enabled' is set to false" }} 3 | {{- end }} 4 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/tests/monitor_test.yaml: -------------------------------------------------------------------------------- 1 | suite: operator prometheus monitor tests 2 | templates: 3 | - monitor.yaml 4 | 5 | tests: 6 | 7 | - it: should not render any ServiceMonitor documents 8 | release: 9 | name: sops 10 | namespace: sops 11 | asserts: 12 | - hasDocuments: 13 | count: 0 14 | 15 | - it: should set correct kind and apiVersion, one document and selector 16 | release: 17 | name: sops 18 | namespace: sops 19 | set: 20 | metrics: 21 | enabled: true 22 | asserts: 23 | - isKind: 24 | of: ServiceMonitor 25 | - isAPIVersion: 26 | of: monitoring.coreos.com/v1 27 | - hasDocuments: 28 | count: 1 29 | - equal: 30 | path: spec.selector.matchLabels 31 | value: 32 | app.kubernetes.io/instance: sops 33 | app.kubernetes.io/name: sops-secrets-operator 34 | 35 | - it: should include additional labels when set 36 | release: 37 | name: sops 38 | namespace: sops 39 | set: 40 | metrics: 41 | enabled: true 42 | additionalLabels: 43 | custom-label: custom-value 44 | asserts: 45 | - hasDocuments: 46 | count: 1 47 | - isKind: 48 | of: ServiceMonitor 49 | - isAPIVersion: 50 | of: monitoring.coreos.com/v1 51 | - equal: 52 | path: metadata.labels 53 | # UPDATE_HERE 54 | value: 55 | app.kubernetes.io/instance: sops 56 | app.kubernetes.io/managed-by: Helm 57 | app.kubernetes.io/name: sops-secrets-operator 58 | app.kubernetes.io/version: "0.16.0" 59 | helm.sh/chart: sops-secrets-operator-0.22.0 60 | custom-label: custom-value 61 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/tests/service_account_test.yaml: -------------------------------------------------------------------------------- 1 | suite: operator service account tests 2 | templates: 3 | - service_account.yaml 4 | 5 | tests: 6 | 7 | # api 8 | - it: should set correct kind and apiVersion 9 | asserts: 10 | - isKind: 11 | of: ServiceAccount 12 | - isAPIVersion: 13 | of: v1 14 | - hasDocuments: 15 | count: 1 16 | 17 | # deployment metadata and labels 18 | - it: should correctly render default service account metadata 19 | release: 20 | name: sops 21 | namespace: sops 22 | asserts: 23 | - equal: 24 | path: metadata.name 25 | value: sops-sops-secrets-operator 26 | - equal: 27 | path: metadata.labels 28 | # UPDATE_HERE 29 | value: 30 | app.kubernetes.io/instance: sops 31 | app.kubernetes.io/managed-by: Helm 32 | app.kubernetes.io/name: sops-secrets-operator 33 | app.kubernetes.io/version: 0.16.0 34 | helm.sh/chart: sops-secrets-operator-0.22.0 35 | 36 | # custom name 37 | - it: should correctly render custome service account name 38 | release: 39 | name: sops 40 | namespace: sops 41 | set: 42 | serviceAccount: 43 | name: AzureSA 44 | asserts: 45 | - equal: 46 | path: metadata.name 47 | value: AzureSA 48 | 49 | # sa disabled 50 | - it: should not render service account if disabled 51 | release: 52 | name: sops 53 | namespace: sops 54 | set: 55 | serviceAccount: 56 | enabled: false 57 | asserts: 58 | - hasDocuments: 59 | count: 0 60 | 61 | # rbac disabled 62 | - it: should not render service account if rbac disabled 63 | release: 64 | name: sops 65 | namespace: sops 66 | set: 67 | rbac: 68 | enabled: false 69 | asserts: 70 | - hasDocuments: 71 | count: 0 72 | 73 | # both sa and rbac are disabled 74 | - it: should not render service account if rbac and sa are disabled 75 | release: 76 | name: sops 77 | namespace: sops 78 | set: 79 | serviceAccount: 80 | enabled: false 81 | rbac: 82 | enabled: false 83 | asserts: 84 | - hasDocuments: 85 | count: 0 86 | 87 | # sa empty annotations 88 | - it: sa annotations should be empty by default 89 | asserts: 90 | - notExists: 91 | path: metadata.annotations 92 | 93 | # sa annotations 94 | - it: sa annotations should be empty by default 95 | set: 96 | serviceAccount: 97 | annotations: 98 | abc: "abc" 99 | asserts: 100 | - equal: 101 | path: metadata.annotations 102 | value: 103 | abc: abc 104 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/tests/validation_test.yaml: -------------------------------------------------------------------------------- 1 | suite: "Validation Tests" 2 | 3 | templates: 4 | - "templates/validation.yaml" 5 | 6 | tests: 7 | - it: "should succeed if '.serviceAccount.enabled' is true (default case)" 8 | set: 9 | serviceAccount: 10 | enabled: true 11 | name: 12 | asserts: 13 | - notFailedTemplate: {} 14 | 15 | - it: "should succeed if '.serviceAccount.enabled' is false and 'name' is set" 16 | set: 17 | serviceAccount: 18 | enabled: false 19 | name: "AzureSA" 20 | asserts: 21 | - notFailedTemplate: {} 22 | 23 | - it: "should fail if '.serviceAccount.enabled' is false and '.serviceAccount.name' is not set" 24 | set: 25 | serviceAccount: 26 | enabled: false 27 | name: "" 28 | asserts: 29 | - failedTemplate: 30 | errorMessage: "Error: serviceAccount 'name' must be set if serviceAccount 'enabled' is set to false" 31 | -------------------------------------------------------------------------------- /chart/helm3/sops-secrets-operator/values.yaml: -------------------------------------------------------------------------------- 1 | # Default values for sops-secrets-operator. 2 | # This is a YAML-formatted file. 3 | # Declare variables to be passed into your templates. 4 | 5 | # https://github.com/norwoodj/helm-docs and https://pre-commit.com/ 6 | # are used to generate documentation automaticaly 7 | 8 | # -- Deployment replica count - should not be modified 9 | replicaCount: 1 10 | 11 | # -- If set - operator will watch SopsSecret resources only in operator namespace 12 | namespaced: false 13 | 14 | # UPDATE_HERE 15 | image: 16 | # -- Operator image name 17 | repository: isindir/sops-secrets-operator 18 | # -- Operator image tag 19 | tag: 0.16.0 20 | # -- Operator image pull policy 21 | pullPolicy: Always 22 | 23 | # UPDATE_HERE 24 | initImage: 25 | # -- Init container image name 26 | repository: ubuntu 27 | # -- Init container image tag 28 | tag: plucky-20250415 29 | # -- Init container image pull policy 30 | pullPolicy: Always 31 | 32 | # -- Secrets to pull image from private docker repository 33 | imagePullSecrets: [] 34 | # -- Overrides auto-generated short resource name 35 | nameOverride: "" 36 | # -- Overrides auto-generated long resource name 37 | fullnameOverride: "" 38 | 39 | # -- Annotations to be added to operator pod 40 | podAnnotations: {} 41 | # -- Labels to be added to operator pod 42 | podLabels: {} 43 | 44 | serviceAccount: 45 | enabled: true 46 | # -- Custom service account name to use instead of automatically generated name (if enabled - chart will generate SA, if not enabled - will use preconfigured) 47 | name: "" 48 | # -- Annotations to be added to the service account 49 | annotations: {} 50 | 51 | # -- Requeue failed reconciliation in minutes (min 1). (default 5) 52 | requeueAfter: 5 53 | 54 | # -- Paths to a kubeconfig. Only required if out-of-cluster. 55 | kubeconfig: 56 | enabled: false 57 | path: 58 | 59 | # -- Logging configuration section suggested values 60 | # Development Mode (encoder=consoleEncoder,logLevel=Debug,stackTraceLevel=Warn). 61 | # Production Mode (encoder=jsonEncoder,logLevel=Info,stackTraceLevel=Error) (default) 62 | logging: 63 | # -- Zap Development Mode enabled 64 | development: false 65 | # -- Zap log encoding (one of 'json' or 'console') 66 | encoder: json 67 | # -- Zap Level to configure the verbosity of logging. Can be one of 'debug', 'info', 'error', or any integer value > 0 which corresponds to custom debug levels of increasing verbosity 68 | level: info 69 | # -- Zap Level at and above which stacktraces are captured (one of 'info', 'error'). 70 | stacktraceLevel: error 71 | # -- Zap time encoding (one of 'epoch', 'millis', 'nano', 'iso8601', 'rfc3339' or 'rfc3339nano'). Defaults to 'epoch'. 72 | timeEncoding: iso8601 73 | 74 | healthProbes: 75 | # -- The address the probe endpoint binds to. (default ":8081") 76 | port: 8081 77 | # -- Liveness probe configuration 78 | liveness: 79 | initialDelaySeconds: 15 80 | periodSeconds: 20 81 | # -- Readiness probe configuration 82 | readiness: 83 | initialDelaySeconds: 5 84 | periodSeconds: 10 85 | 86 | # -- GPG configuration section 87 | gpg: 88 | # -- If `true` GCP secret will be created from provided value and mounted as environment variable 89 | enabled: false 90 | # -- Name of the secret to create - will override default secret name if specified 91 | secret1: gpg1 92 | # -- Name of the secret to create - will override default secret name if specified 93 | secret2: gpg2 94 | 95 | # -- GCP KMS configuration section 96 | gcp: 97 | # -- Node labels for operator pod assignment 98 | enabled: false 99 | # -- Name of the secret to create - will override default secret name if specified 100 | svcAccSecretCustomName: '' 101 | # -- If `gcp.enabled` is `true`, this value must be specified as GCP service account secret json payload 102 | svcAccSecret: '' 103 | # -- Name of a pre-existing secret containing GCP service account secret json payload 104 | existingSecretName: '' 105 | 106 | # -- Azure KeyVault configuration section 107 | azure: 108 | # Specify credentials here or use existingSecretName below to use a pre-configred secret 109 | 110 | # -- if true Azure KeyVault will be used 111 | enabled: false 112 | # -- TenantID of Azure Service principal to use 113 | tenantId: '' 114 | # -- ClientID (Application ID) of Azure Service Principal to use 115 | clientId: '' 116 | # -- Client Secret of Azure Service Principal 117 | clientSecret: '' 118 | # Pre-existing secret must contain the keys tenantId, clientId and clientSecret with the appropriate values 119 | # -- Name of a pre-existing secret containing Azure Service Principal Credentials (ClientID, ClientSecret, TenantID) 120 | existingSecretName: '' 121 | 122 | # -- A list of additional environment variables 123 | extraEnv: [] 124 | #- name: AWS_SDK_LOAD_CONFIG 125 | # value: "1" 126 | 127 | # -- configure custom secrets to be used as environment variables at runtime, see values.yaml 128 | secretsAsEnvVars: [] 129 | #- name: SECRET_GREETING 130 | # secretName: my-secret-greeting 131 | # secretKey: greeting 132 | 133 | # -- configure custom secrets to be mounted at runtime, see values.yaml 134 | secretsAsFiles: [] 135 | # All files within secret will be mounted in "/etc/foo" - same as 1st example in k8s documentation 136 | # all secrets will be mounted as readonly 137 | #- name: foo 138 | # mountPath: "/etc/foo" 139 | # secretName: mysecret 140 | 141 | # -- Operator container resources 142 | resources: {} 143 | # We usually recommend not to specify default resources and to leave this as a conscious 144 | # choice for the user. This also increases chances charts run on environments with little 145 | # resources, such as Minikube. If you do want to specify resources, uncomment the following 146 | # lines, adjust them as necessary, and remove the curly braces after 'resources:'. 147 | # limits: 148 | # cpu: 500m 149 | # memory: 128Mi 150 | # requests: 151 | # cpu: 10m 152 | # memory: 64Mi 153 | 154 | # -- Node selector to use for pod configuration 155 | nodeSelector: {} 156 | 157 | securityContext: 158 | # -- Enable securityContext 159 | enabled: false 160 | # -- UID to run as 161 | runAsUser: 13001 162 | # -- GID to run as 163 | runAsGroup: 13001 164 | # -- Enable kubelet validation for using root user to run container 165 | runAsNonRoot: true 166 | # -- fs group 167 | fsGroup: 13001 168 | # -- seccompProfile.type 169 | seccompProfileType: RuntimeDefault 170 | # -- if seccompProfile.type is set to Localhost, set localhostProfile to value of seccompProfileName (user must specify value) 171 | seccompProfileName: "" 172 | # -- container/initContainer 173 | container: 174 | # -- enables securityContext capabilities feature in containers 175 | enabled: false 176 | # -- capabilities 177 | capabilities: 178 | drop: 179 | - all 180 | add: 181 | - NET_BIND_SERVICE 182 | 183 | # -- Tolerations to be applied to operator pod 184 | tolerations: [] 185 | 186 | # -- Node affinity for pod assignment 187 | affinity: {} 188 | 189 | rbac: 190 | # -- Create and use RBAC resources 191 | enabled: true 192 | 193 | metrics: 194 | # -- Enable prometheus metrics 195 | enabled: false 196 | # -- Additional labels for ServiceMonitor 197 | additionalLabels: {} 198 | -------------------------------------------------------------------------------- /chart/samples: -------------------------------------------------------------------------------- 1 | ../config/samples -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 | 5 | package main 6 | 7 | import ( 8 | "flag" 9 | "fmt" 10 | "os" 11 | 12 | "sigs.k8s.io/controller-runtime/pkg/cache" 13 | metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" 14 | 15 | // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) 16 | // to ensure that exec-entrypoint and run can make use of them. 17 | _ "k8s.io/client-go/plugin/pkg/client/auth" 18 | 19 | "k8s.io/apimachinery/pkg/runtime" 20 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 21 | clientgoscheme "k8s.io/client-go/kubernetes/scheme" 22 | _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" 23 | ctrl "sigs.k8s.io/controller-runtime" 24 | "sigs.k8s.io/controller-runtime/pkg/healthz" 25 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 26 | 27 | isindirv1alpha1 "github.com/isindir/sops-secrets-operator/api/v1alpha1" 28 | isindirv1alpha2 "github.com/isindir/sops-secrets-operator/api/v1alpha2" 29 | isindirv1alpha3 "github.com/isindir/sops-secrets-operator/api/v1alpha3" 30 | "github.com/isindir/sops-secrets-operator/internal/controllers" 31 | //+kubebuilder:scaffold:imports 32 | ) 33 | 34 | var ( 35 | scheme = runtime.NewScheme() 36 | setupLog = ctrl.Log.WithName("setup") 37 | ) 38 | 39 | func init() { 40 | utilruntime.Must(clientgoscheme.AddToScheme(scheme)) 41 | 42 | utilruntime.Must(isindirv1alpha3.AddToScheme(scheme)) 43 | utilruntime.Must(isindirv1alpha2.AddToScheme(scheme)) 44 | utilruntime.Must(isindirv1alpha1.AddToScheme(scheme)) 45 | //+kubebuilder:scaffold:scheme 46 | } 47 | 48 | func main() { 49 | var metricsAddr string 50 | var enableLeaderElection bool 51 | var probeAddr string 52 | var requeueAfter int64 53 | var watchNamespace string 54 | 55 | flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") 56 | flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") 57 | flag.BoolVar(&enableLeaderElection, "leader-elect", false, 58 | "Enable leader election for controller manager. "+ 59 | "Enabling this will ensure there is only one active controller manager.") 60 | flag.Int64Var(&requeueAfter, "requeue-decrypt-after", 5, "Requeue failed reconciliation in minutes (min 1).") 61 | flag.StringVar(&watchNamespace, "watch-namespace", "", "Namespace to watch for SopsSecret objects (default: all namespaces).") 62 | 63 | opts := zap.Options{ 64 | Development: true, 65 | } 66 | 67 | opts.BindFlags(flag.CommandLine) 68 | flag.Parse() 69 | 70 | ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) 71 | 72 | cacheOptions := cache.Options{} 73 | if watchNamespace != "" { 74 | cacheOptions.DefaultNamespaces = map[string]cache.Config{ 75 | watchNamespace: {}, 76 | } 77 | setupLog.V(0).Info(fmt.Sprintf("Watching SopsSecret objects in namespace %s", watchNamespace)) 78 | } else { 79 | setupLog.V(0).Info("Watching SopsSecret objects in all namespaces") 80 | } 81 | 82 | mgr, err := ctrl.NewManager( 83 | ctrl.GetConfigOrDie(), 84 | ctrl.Options{ 85 | Scheme: scheme, 86 | Cache: cacheOptions, 87 | Metrics: metricsserver.Options{ 88 | BindAddress: metricsAddr, 89 | }, 90 | HealthProbeBindAddress: probeAddr, 91 | LeaderElection: enableLeaderElection, 92 | LeaderElectionID: "ca57d051.github.com", 93 | }, 94 | ) 95 | if err != nil { 96 | setupLog.Error(err, "unable to start manager") 97 | os.Exit(1) 98 | } 99 | 100 | if requeueAfter < 1 { 101 | requeueAfter = 1 102 | } 103 | 104 | setupLog.V(0).Info( 105 | fmt.Sprintf( 106 | "SopsSecret reconciliation will be requeued after %d minutes after decryption failures", 107 | requeueAfter, 108 | ), 109 | ) 110 | 111 | if err = (&controllers.SopsSecretReconciler{ 112 | Client: mgr.GetClient(), 113 | Log: ctrl.Log.WithName("controllers").WithName("SopsSecret"), 114 | Scheme: mgr.GetScheme(), 115 | RequeueAfter: requeueAfter, 116 | }).SetupWithManager(mgr); err != nil { 117 | setupLog.Error(err, "unable to create controller", "controller", "SopsSecret") 118 | os.Exit(1) 119 | } 120 | //+kubebuilder:scaffold:builder 121 | 122 | if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { 123 | setupLog.Error(err, "unable to set up health check") 124 | os.Exit(1) 125 | } 126 | if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil { 127 | setupLog.Error(err, "unable to set up ready check") 128 | os.Exit(1) 129 | } 130 | 131 | setupLog.Info("starting manager") 132 | if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil { 133 | setupLog.Error(err, "problem running manager") 134 | os.Exit(1) 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /config/age-test-key/00-raw-test-secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: isindir.github.com/v1alpha3 2 | kind: SopsSecret 3 | metadata: 4 | name: test-sopssecret 5 | namespace: default 6 | spec: 7 | secretTemplates: 8 | - name: test-stringdata-token 9 | stringData: 10 | token: Wb4ziZdELkdUf6m6KtNd7iRjjQRvSeJno5meH4NAGHFmpqJyEsekZ2WjX232s4Gj 11 | - name: test-data-token 12 | data: 13 | token: V2I0emlaZEVMa2RVZjZtNkt0TmQ3aVJqalFSdlNlSm5vNW1lSDROQUdIRm1wcUp5RXNla1oyV2pYMjMyczRHag== 14 | - name: test-labels-annotations-jenkins-secret 15 | labels: 16 | "jenkins.io/credentials-type": "usernamePassword" 17 | annotations: 18 | "jenkins.io/credentials-description" : "credentials from Kubernetes" 19 | stringData: 20 | username: myUsername 21 | password: 'Pa$$word' 22 | - name: test-type-docker-login 23 | type: 'kubernetes.io/dockerconfigjson' 24 | stringData: 25 | .dockerconfigjson: '{"auths":{"index.docker.io":{"username":"imyuser","password":"mypass","email":"myuser@abc.com","auth":"aW15dXNlcjpteXBhc3M="}}}' 26 | - name: test-type-custom-secret-type 27 | type: 'custom/type' 28 | stringData: 29 | username: some-username 30 | password: 'some-password!' 31 | -------------------------------------------------------------------------------- /config/age-test-key/00-test-secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: isindir.github.com/v1alpha3 2 | kind: SopsSecret 3 | metadata: 4 | name: test-sopssecret 5 | namespace: default 6 | spec: 7 | secretTemplates: 8 | - name: ENC[AES256_GCM,data:6V4Ucx7n6A11VkrZtCiyqUSeKKFC,iv:5E1Xu6eFqkE4kM2q7Yf9dtoc8399uyEhfnj2Ni9BUc0=,tag:J8NkiHxnG6stFZkGF090gA==,type:str] 9 | stringData: 10 | token: ENC[AES256_GCM,data:A5Q72PY++0z7uKfIX2jSHHcOBoLj3MltCnPPl6v74MmflcqmZRCC3fR1EdzaaOAj1ILKJHBMMKf73eLo/W+Cmw==,iv:/xpIAjjX05PmdPznshlO0vsHR3irXHhDV6YdjL6n6w4=,tag:tDfGF+NVVnoYGw78N1/UAg==,type:str] 11 | - name: ENC[AES256_GCM,data:pCSWLt3EBykhIClt9t8W,iv:CdzcJzAZFIpnvcUcQQyHAuIdYcp2S3ufwrGiWKyOCFo=,tag:xB6jysJ1LcI1kpvwuBpOoQ==,type:str] 12 | data: 13 | token: ENC[AES256_GCM,data:1eRxH3SpEsvL/LCgV0uTdbZUzq1aoTa2/urzJKf/CStET4nbCpqDR8VIvl4sb7Ym4UdblKggAd0IxcVCuQEnpdnLs0fhRi0aaVqyJBKjD4ewmFamNpBWEA==,iv:tgR7wSjxezstm+e1WuMLlZ6Gs6zVkfo6yDavOUZV4OA=,tag:STLqjwHQqXcQ/xPWrV9Skg==,type:str] 14 | - name: ENC[AES256_GCM,data:YPpvKUryfv2HRzeSaPV0WfBG3Ob4Ag26Lvy4CmylMA5wPkKqaZQ=,iv:PCn6Zp+iEsF+CSK7An+VkXhg1JD3TJ+nE8gVeKTg2T0=,tag:MuLpy+KxHWCevJUxq/tFbg==,type:str] 15 | labels: 16 | jenkins.io/credentials-type: ENC[AES256_GCM,data:Wrqn2XG5KjUKOzzBcRrJTQ==,iv:NEm+ZM5KBK+eF/eWgdNcT1190eIjOEaGTNamVfveXLY=,tag:DejUX7y+CC2V7qQZbN5GNw==,type:str] 17 | annotations: 18 | jenkins.io/credentials-description: ENC[AES256_GCM,data:6DxJ5dSqKPd5cTlVc2h+cvcdCYxVXnHvKIp8,iv:KeBAk4B8je2qKxV5xPMU+5i9v3UI3bIeY1NdhvSyRPg=,tag:Cw/rPrYy/PRcdUcMzUNYQQ==,type:str] 19 | stringData: 20 | username: ENC[AES256_GCM,data:2331v9GLgGmspQ==,iv:3gOdC9ICqE/IGOoTESCpOBb6Z+MV4pJ/e1T/WulLPtg=,tag:OQILlBVUoBNodkhvrI5IlQ==,type:str] 21 | password: ENC[AES256_GCM,data:YKZtVH5w5uRNTds=,iv:mK/us3zPUtKa1nUv+Lw8A9DZHvuoHsX5i3i0G/9XJLc=,tag:+HMWTSQeYD9HK3fYldzeGw==,type:str] 22 | - name: ENC[AES256_GCM,data:AdqTEEJdkr61Lr6Rgc7n/lh7vwoclA==,iv:8ROnJOl78gE9i2TBKyhAa4RhnQmNnIBgNjlupJlrhCA=,tag:OMYJjY+MmXH/9AYnF7uJqg==,type:str] 23 | type: ENC[AES256_GCM,data:rLjSr2aByTeACO2ucCj6h81qdS9QNmLs2QTB4wjJ,iv:KBmpgMCaK79uVdAsVxZMRqe1CQGDXxPESvwKtSPh6+s=,tag:s/4f6MnEIDoj1doDYUjaHQ==,type:str] 24 | stringData: 25 | .dockerconfigjson: ENC[AES256_GCM,data:EBB6l/PQVfEMCg6MYe1yUwGV8RxPyAUMom7DvtTDdce8lY1wztEWyrmCHPLXE/IkIq3/Iz6QTyyMFFBUERJovz5aYSwBAMeX6ocle9SG3wMWe1/cySYIQednL+PF1GWNUaIn1MEMO7PP1VopEuZSdWtHvXxfrubWC2mRMmhHaw==,iv:HV/C0ceSQcFveWzXBVZMfGSHV/bRBeB7ka/zxrHnrY8=,tag:HILN9fQBDbWFxjjnkBkp4Q==,type:str] 26 | - name: ENC[AES256_GCM,data:s4jnBB5wPZ8ZLTlbmZ7ClwvhnMo9GX6cOy8CqQ==,iv:wv7Rnnf1KWSLeOAy1uB+eDRVzNUmaSAt1A14Gm7Q3SQ=,tag:6fejkzBv7aXzDIS3ueUSNg==,type:str] 27 | type: ENC[AES256_GCM,data:5vi6NTNHWsO8hqE=,iv:5UwCj6ntDKYWFMHIijg8SLvXCDXxuPrDQBxV91eWizY=,tag:1XSPmM63vdrl5r/151kbfg==,type:str] 28 | stringData: 29 | username: ENC[AES256_GCM,data:HbwAwIhj6CElkHMpZg==,iv:McHE5IZ0EtYblv8LhwJwfE/W50Biq8FuT02ji+VWxaQ=,tag:0sUtO8rTVd7aDcDk5wZtFA==,type:str] 30 | password: ENC[AES256_GCM,data:q+9LX8KsGQT5XDPzCrg=,iv:59TmGNnu2mGkMiqrT5Vg+COS8D3u3HbpluQAtyzH9fk=,tag:DxRE/zN4uN+TbT7H7kbzYw==,type:str] 31 | sops: 32 | kms: [] 33 | gcp_kms: [] 34 | azure_kv: [] 35 | hc_vault: [] 36 | age: 37 | - recipient: age1pnmp2nq5qx9z4lpmachyn2ld07xjumn98hpeq77e4glddu96zvms9nn7c8 38 | enc: | 39 | -----BEGIN AGE ENCRYPTED FILE----- 40 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoNUpVbVhNbWluZjFyRHhT 41 | OWoxYU9oU1h6dFYycGVMb2RJTlhWMFhPTVFnCkQxb3liRG9TS096TFN4UVJFK25Y 42 | K2x5SG53dmM4ZHF2dDZWdXdDVHN3Yk0KLS0tIE1xTlN1Unk4aURZWFNoNUhGMGc3 43 | YzlyY0NwQnptejk2RWpRV3RMUDVRWm8KDsF8k67qcYafOPIiAYVeQ+SvzLobq0pg 44 | h8OmBkNaahywzLTAjEcy8j84JRa1muAEJEX4fCLzE+7hsN+11Yc2gA== 45 | -----END AGE ENCRYPTED FILE----- 46 | lastmodified: "2023-08-09T08:06:03Z" 47 | mac: ENC[AES256_GCM,data:/GIB9ibqMFO71y9Mf0Jg87dsWXY4VJTQweNpVLhh3H0wYeZgZEG5+3g97Lah+UqEFhrHCXtH3ZiRanGLVgCz3jiFKEGOm56WyoA/Qn8NNRuH5k8KEOClFcC7vp0lSZ0xqcUhY+GoNQzybu4/K4MtSmMnwHfTXGKDZrjNPsuZF5g=,iv:VM818u90AEW5f/xSGKKgXZD4m0vZQhva7faoCTnYKOk=,tag:mC6f9l1YxHyKqFbCS4S70Q==,type:str] 48 | pgp: [] 49 | encrypted_suffix: Templates 50 | version: 3.7.3 51 | -------------------------------------------------------------------------------- /config/age-test-key/01-raw-test-secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: isindir.github.com/v1alpha3 2 | kind: SopsSecret 3 | metadata: 4 | name: test-sopssecret-01 5 | namespace: default 6 | spec: 7 | secretTemplates: 8 | - name: test-labels-annotations-jenkins-secret-01 9 | labels: 10 | jenkins.io/credentials-type: usernamePassword 11 | annotations: 12 | jenkins.io/credentials-description: credentials from Kubernetes 13 | stringData: 14 | username: myUsername 15 | password: Pa58163word 16 | -------------------------------------------------------------------------------- /config/age-test-key/01-test-secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: isindir.github.com/v1alpha3 2 | kind: SopsSecret 3 | metadata: 4 | name: test-sopssecret-01 5 | namespace: default 6 | spec: 7 | secretTemplates: 8 | - name: ENC[AES256_GCM,data:A7H4IVumiTfcexzxmu9nwmjiKArrH8acvQg40w2ZuyId1TlJ2ST1ilc=,iv:GvV0lni/tLF1/WKZV3RYDbtrPWlAP+R8NJOjf9h58+A=,tag:q9WLOUBK6PYhH7XvBO1PHA==,type:str] 9 | labels: 10 | jenkins.io/credentials-type: ENC[AES256_GCM,data:P7eWw3+I43Pp6CZPgT5hEQ==,iv:Fl82eZ5daoKEKJvhOKvmjG49AE0Com4iasDinhVav2s=,tag:24HRqNjf1lX9pnvjjqVzTQ==,type:str] 11 | annotations: 12 | jenkins.io/credentials-description: ENC[AES256_GCM,data:6DxJ5dSqKPd5cTlVc2h+cvcdCYxVXnHvKIp8,iv:KeBAk4B8je2qKxV5xPMU+5i9v3UI3bIeY1NdhvSyRPg=,tag:Cw/rPrYy/PRcdUcMzUNYQQ==,type:str] 13 | stringData: 14 | username: ENC[AES256_GCM,data:3331v9GLgGmspQ==,iv:3gOdC9ICqE/IGOoTESCpOBb6Z+MV4pJ/e1T/WulLPtg=,tag:OQILlBVUoBNodkhvrI5IlQ==,type:str] 15 | password: ENC[AES256_GCM,data:YKZtVH5w5uRNTds=,iv:mK/us3zPUtKa1nUv+Lw8A9DZHvuoHsX5i3i0G/9XJLc=,tag:+HMWTSQeYD9HK3fYldzeGw==,type:str] 16 | sops: 17 | kms: [] 18 | gcp_kms: [] 19 | azure_kv: [] 20 | hc_vault: [] 21 | age: 22 | - recipient: age1pnmp2nq5qx9z4lpmachyn2ld07xjumn98hpeq77e4glddu96zvms9nn7c8 23 | enc: | 24 | -----BEGIN AGE ENCRYPTED FILE----- 25 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoNUpVbVhNbWluZjFyRHhT 26 | OWoxYU9oU1h6dFYycGVMb2RJTlhWMFhPTVFnCkQxb3liRG9TS096TFN4UVJFK25Y 27 | K2x5SG53dmM4ZHF2dDZWdXdDVHN3Yk0KLS0tIE1xTlN1Unk4aURZWFNoNUhGMGc3 28 | YzlyY0NwQnptejk2RWpRV3RMUDVRWm8KDsF8k67qcYafOPIiAYVeQ+SvzLobq0pg 29 | h8OmBkNaahywzLTAjEcy8j84JRa1muAEJEX4fCLzE+7hsN+11Yc2gA== 30 | -----END AGE ENCRYPTED FILE----- 31 | lastmodified: "2022-10-31T19:17:27Z" 32 | mac: ENC[AES256_GCM,data:1AXynHRgFPJ+4c4hw/V25ENnJwDzYkVet4HRSLymA7ZVoq/MJlnIgksY4/hcbwnMA5U4OPToYZDpOhkGPTf1cGjkS/bTctU6T5RSpFFRWFKBEgfmWpyEgYa/tVywzBHoFlfgHaatR1kqo4uWdNEkHiyEYIU6KlcdC50cEXOeHQs=,iv:GzmJ3KQ2kiluKQWzfublfogp086ma/elgbpP2B35bRY=,tag:Om3+2Gz6NTy+B8e6vVCBfA==,type:str] 33 | pgp: [] 34 | encrypted_suffix: Templates 35 | version: 3.7.3 36 | -------------------------------------------------------------------------------- /config/age-test-key/02-raw-test-secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: isindir.github.com/v1alpha3 2 | kind: SopsSecret 3 | metadata: 4 | name: test-sopssecret-02 5 | namespace: default 6 | spec: 7 | secretTemplates: 8 | - name: not-owned-secret-02 9 | stringData: 10 | username: myUsername 11 | password: Pa58163word 12 | -------------------------------------------------------------------------------- /config/age-test-key/02-test-secrets.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: isindir.github.com/v1alpha3 2 | kind: SopsSecret 3 | metadata: 4 | name: test-sopssecret-02 5 | namespace: default 6 | spec: 7 | secretTemplates: 8 | - name: ENC[AES256_GCM,data:O8FnRgETP8Ehr1Wd9vH7QkFbrQ==,iv:JK1tzSqEQaptdEO47gWgidlplF8v46TuxoDNMvmmFg4=,tag:wgOei8l2F+Xwvt8V8WlzNw==,type:str] 9 | stringData: 10 | username: ENC[AES256_GCM,data:2331v9GLgGmspQ==,iv:3gOdC9ICqE/IGOoTESCpOBb6Z+MV4pJ/e1T/WulLPtg=,tag:OQILlBVUoBNodkhvrI5IlQ==,type:str] 11 | password: ENC[AES256_GCM,data:YKZtVH5w5uRNTds=,iv:mK/us3zPUtKa1nUv+Lw8A9DZHvuoHsX5i3i0G/9XJLc=,tag:+HMWTSQeYD9HK3fYldzeGw==,type:str] 12 | sops: 13 | kms: [] 14 | gcp_kms: [] 15 | azure_kv: [] 16 | hc_vault: [] 17 | age: 18 | - recipient: age1pnmp2nq5qx9z4lpmachyn2ld07xjumn98hpeq77e4glddu96zvms9nn7c8 19 | enc: | 20 | -----BEGIN AGE ENCRYPTED FILE----- 21 | YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBoNUpVbVhNbWluZjFyRHhT 22 | OWoxYU9oU1h6dFYycGVMb2RJTlhWMFhPTVFnCkQxb3liRG9TS096TFN4UVJFK25Y 23 | K2x5SG53dmM4ZHF2dDZWdXdDVHN3Yk0KLS0tIE1xTlN1Unk4aURZWFNoNUhGMGc3 24 | YzlyY0NwQnptejk2RWpRV3RMUDVRWm8KDsF8k67qcYafOPIiAYVeQ+SvzLobq0pg 25 | h8OmBkNaahywzLTAjEcy8j84JRa1muAEJEX4fCLzE+7hsN+11Yc2gA== 26 | -----END AGE ENCRYPTED FILE----- 27 | lastmodified: "2022-10-31T19:42:19Z" 28 | mac: ENC[AES256_GCM,data:BYtw1PA3S4lj5HQE8V1eb2OXYin3wlKA8VXSgAp+LoTX96m4SJFjqERgdVWXuGiO7dt0fBAanCqlN3Re1IcJQutS+W1i9c1lEASHGWXTAF2YS+cHYFHYDT0l0nmjMyWqRZ0H7dZdJKMOxB213Wq4paP7YzbHUj0wdv0SIFxsbKs=,iv:wIKIBaQLR3v65DiDbtJt9IjyoqhooYnxUGfBLQ7MQoE=,tag:eVraoTWD8Yh8KILVhWgCgw==,type:str] 29 | pgp: [] 30 | encrypted_suffix: Templates 31 | version: 3.7.3 32 | -------------------------------------------------------------------------------- /config/age-test-key/key-file.txt: -------------------------------------------------------------------------------- 1 | # created: 2021-08-20T15:53:30+01:00 2 | # public key: age1pnmp2nq5qx9z4lpmachyn2ld07xjumn98hpeq77e4glddu96zvms9nn7c8 3 | AGE-SECRET-KEY-1CQ3KTYJ25YDYA2XVYFGH8P5UJWCENLJ02ZRHMH9YV84WKQVGP3SS07GYNK 4 | -------------------------------------------------------------------------------- /config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This kustomization.yaml is not intended to be run by itself, 2 | # since it depends on service name and namespace that are out of this kustomize package. 3 | # It should be run by config/default 4 | resources: 5 | - bases/isindir.github.com_sopssecrets.yaml 6 | #+kubebuilder:scaffold:crdkustomizeresource 7 | 8 | patchesStrategicMerge: 9 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. 10 | # patches here are for enabling the conversion webhook for each CRD 11 | #- patches/webhook_in_sopssecrets.yaml 12 | #+kubebuilder:scaffold:crdkustomizewebhookpatch 13 | 14 | # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. 15 | # patches here are for enabling the CA injection for each CRD 16 | #- patches/cainjection_in_sopssecrets.yaml 17 | #+kubebuilder:scaffold:crdkustomizecainjectionpatch 18 | 19 | # the following config is for teaching kustomize how to do kustomization for CRDs. 20 | configurations: 21 | - kustomizeconfig.yaml 22 | -------------------------------------------------------------------------------- /config/crd/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD 2 | nameReference: 3 | - kind: Service 4 | version: v1 5 | fieldSpecs: 6 | - kind: CustomResourceDefinition 7 | version: v1 8 | group: apiextensions.k8s.io 9 | path: spec/conversion/webhook/clientConfig/service/name 10 | 11 | namespace: 12 | - kind: CustomResourceDefinition 13 | version: v1 14 | group: apiextensions.k8s.io 15 | path: spec/conversion/webhook/clientConfig/service/namespace 16 | create: false 17 | 18 | varReference: 19 | - path: metadata/annotations 20 | -------------------------------------------------------------------------------- /config/crd/patches/cainjection_in_sopssecrets.yaml: -------------------------------------------------------------------------------- 1 | # The following patch adds a directive for certmanager to inject CA into the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) 7 | name: sopssecrets.isindir.github.com 8 | -------------------------------------------------------------------------------- /config/crd/patches/webhook_in_sopssecrets.yaml: -------------------------------------------------------------------------------- 1 | # The following patch enables a conversion webhook for the CRD 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | name: sopssecrets.isindir.github.com 6 | spec: 7 | conversion: 8 | strategy: Webhook 9 | webhook: 10 | clientConfig: 11 | service: 12 | namespace: system 13 | name: webhook-service 14 | path: /convert 15 | -------------------------------------------------------------------------------- /config/default/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # Adds namespace to all resources. 2 | namespace: sops-secrets-operator-system 3 | 4 | # Value of this field is prepended to the 5 | # names of all resources, e.g. a deployment named 6 | # "wordpress" becomes "alices-wordpress". 7 | # Note that it should also match with the prefix (text before '-') of the namespace 8 | # field above. 9 | namePrefix: sops-secrets-operator- 10 | 11 | # Labels to add to all resources and selectors. 12 | #commonLabels: 13 | # someName: someValue 14 | 15 | bases: 16 | - ../crd 17 | - ../rbac 18 | - ../manager 19 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 20 | # crd/kustomization.yaml 21 | #- ../webhook 22 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. 23 | #- ../certmanager 24 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. 25 | #- ../prometheus 26 | 27 | patchesStrategicMerge: 28 | # Protect the /metrics endpoint by putting it behind auth. 29 | # If you want your controller-manager to expose the /metrics 30 | # endpoint w/o any authn/z, please comment the following line. 31 | - manager_auth_proxy_patch.yaml 32 | 33 | # Mount the controller config file for loading manager configurations 34 | # through a ComponentConfig type 35 | #- manager_config_patch.yaml 36 | 37 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in 38 | # crd/kustomization.yaml 39 | #- manager_webhook_patch.yaml 40 | 41 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 42 | # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. 43 | # 'CERTMANAGER' needs to be enabled to use ca injection 44 | #- webhookcainjection_patch.yaml 45 | 46 | # the following config is for teaching kustomize how to do var substitution 47 | vars: 48 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. 49 | #- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR 50 | # objref: 51 | # kind: Certificate 52 | # group: cert-manager.io 53 | # version: v1 54 | # name: serving-cert # this name should match the one in certificate.yaml 55 | # fieldref: 56 | # fieldpath: metadata.namespace 57 | #- name: CERTIFICATE_NAME 58 | # objref: 59 | # kind: Certificate 60 | # group: cert-manager.io 61 | # version: v1 62 | # name: serving-cert # this name should match the one in certificate.yaml 63 | #- name: SERVICE_NAMESPACE # namespace of the service 64 | # objref: 65 | # kind: Service 66 | # version: v1 67 | # name: webhook-service 68 | # fieldref: 69 | # fieldpath: metadata.namespace 70 | #- name: SERVICE_NAME 71 | # objref: 72 | # kind: Service 73 | # version: v1 74 | # name: webhook-service 75 | -------------------------------------------------------------------------------- /config/default/manager_auth_proxy_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch inject a sidecar container which is a HTTP proxy for the 2 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. 3 | apiVersion: apps/v1 4 | kind: Deployment 5 | metadata: 6 | name: controller-manager 7 | namespace: system 8 | spec: 9 | template: 10 | spec: 11 | containers: 12 | - name: kube-rbac-proxy 13 | image: gcr.io/kubebuilder/kube-rbac-proxy:v0.8.0 14 | args: 15 | - "--secure-listen-address=0.0.0.0:8443" 16 | - "--upstream=http://127.0.0.1:8080/" 17 | - "--logtostderr=true" 18 | - "--v=10" 19 | ports: 20 | - containerPort: 8443 21 | protocol: TCP 22 | name: https 23 | - name: manager 24 | args: 25 | - "--health-probe-bind-address=:8081" 26 | - "--metrics-bind-address=127.0.0.1:8080" 27 | - "--leader-elect" 28 | -------------------------------------------------------------------------------- /config/default/manager_config_patch.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | spec: 7 | template: 8 | spec: 9 | containers: 10 | - name: manager 11 | args: 12 | - "--config=controller_manager_config.yaml" 13 | volumeMounts: 14 | - name: manager-config 15 | mountPath: /controller_manager_config.yaml 16 | subPath: controller_manager_config.yaml 17 | volumes: 18 | - name: manager-config 19 | configMap: 20 | name: manager-config 21 | -------------------------------------------------------------------------------- /config/manager/controller_manager_config.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 2 | kind: ControllerManagerConfig 3 | health: 4 | healthProbeBindAddress: :8081 5 | metrics: 6 | bindAddress: 127.0.0.1:8080 7 | webhook: 8 | port: 9443 9 | leaderElection: 10 | leaderElect: true 11 | resourceName: ca57d051.github.com 12 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | 4 | generatorOptions: 5 | disableNameSuffixHash: true 6 | 7 | configMapGenerator: 8 | - name: manager-config 9 | files: 10 | - controller_manager_config.yaml 11 | -------------------------------------------------------------------------------- /config/manager/manager.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Namespace 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: system 7 | --- 8 | apiVersion: apps/v1 9 | kind: Deployment 10 | metadata: 11 | name: controller-manager 12 | namespace: system 13 | labels: 14 | control-plane: controller-manager 15 | spec: 16 | selector: 17 | matchLabels: 18 | control-plane: controller-manager 19 | replicas: 1 20 | template: 21 | metadata: 22 | annotations: 23 | kubectl.kubernetes.io/default-container: manager 24 | labels: 25 | control-plane: controller-manager 26 | spec: 27 | securityContext: 28 | runAsNonRoot: true 29 | containers: 30 | - command: 31 | - /usr/local/bin/manager 32 | args: 33 | - --leader-elect 34 | - "--requeue-decrypt-after=5" 35 | image: controller:latest 36 | name: manager 37 | securityContext: 38 | allowPrivilegeEscalation: false 39 | livenessProbe: 40 | httpGet: 41 | path: /healthz 42 | port: 8081 43 | initialDelaySeconds: 15 44 | periodSeconds: 20 45 | readinessProbe: 46 | httpGet: 47 | path: /readyz 48 | port: 8081 49 | initialDelaySeconds: 5 50 | periodSeconds: 10 51 | # TODO(user): Configure the resources accordingly based on the project requirements. 52 | # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ 53 | resources: 54 | limits: 55 | cpu: 500m 56 | memory: 128Mi 57 | requests: 58 | cpu: 10m 59 | memory: 64Mi 60 | env: 61 | - name: WATCH_NAMESPACE 62 | valueFrom: 63 | fieldRef: 64 | fieldPath: metadata.namespace 65 | serviceAccountName: controller-manager 66 | terminationGracePeriodSeconds: 10 67 | -------------------------------------------------------------------------------- /config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | 2 | # Prometheus Monitor Service (Metrics) 3 | apiVersion: monitoring.coreos.com/v1 4 | kind: ServiceMonitor 5 | metadata: 6 | labels: 7 | control-plane: controller-manager 8 | name: controller-manager-metrics-monitor 9 | namespace: system 10 | spec: 11 | endpoints: 12 | - path: /metrics 13 | port: https 14 | scheme: https 15 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 16 | tlsConfig: 17 | insecureSkipVerify: true 18 | selector: 19 | matchLabels: 20 | control-plane: controller-manager 21 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_client_clusterrole.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: 7 | - "/metrics" 8 | verbs: 9 | - get 10 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: proxy-role 5 | rules: 6 | - apiGroups: 7 | - authentication.k8s.io 8 | resources: 9 | - tokenreviews 10 | verbs: 11 | - create 12 | - apiGroups: 13 | - authorization.k8s.io 14 | resources: 15 | - subjectaccessreviews 16 | verbs: 17 | - create 18 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: proxy-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: proxy-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/auth_proxy_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | name: controller-manager-metrics-service 7 | namespace: system 8 | spec: 9 | ports: 10 | - name: https 11 | port: 8443 12 | targetPort: https 13 | selector: 14 | control-plane: controller-manager 15 | -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | # All RBAC will be applied under this service account in 3 | # the deployment namespace. You may comment out this resource 4 | # if your manager will use a service account that exists at 5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding 6 | # subjects if changing service account names. 7 | - service_account.yaml 8 | - role.yaml 9 | - role_binding.yaml 10 | - leader_election_role.yaml 11 | - leader_election_role_binding.yaml 12 | # Comment the following 4 lines if you want to disable 13 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy) 14 | # which protects your /metrics endpoint. 15 | - auth_proxy_service.yaml 16 | - auth_proxy_role.yaml 17 | - auth_proxy_role_binding.yaml 18 | - auth_proxy_client_clusterrole.yaml 19 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | name: leader-election-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - configmaps 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - create 16 | - update 17 | - patch 18 | - delete 19 | - apiGroups: 20 | - coordination.k8s.io 21 | resources: 22 | - leases 23 | verbs: 24 | - get 25 | - list 26 | - watch 27 | - create 28 | - update 29 | - patch 30 | - delete 31 | - apiGroups: 32 | - "" 33 | resources: 34 | - events 35 | verbs: 36 | - create 37 | - patch 38 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | name: leader-election-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: Role 8 | name: leader-election-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: manager-role 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - secrets 11 | verbs: 12 | - '*' 13 | - apiGroups: 14 | - "" 15 | resources: 16 | - secrets/status 17 | verbs: 18 | - get 19 | - patch 20 | - update 21 | - apiGroups: 22 | - isindir.github.com 23 | resources: 24 | - sopssecrets 25 | verbs: 26 | - create 27 | - delete 28 | - get 29 | - list 30 | - patch 31 | - update 32 | - watch 33 | - apiGroups: 34 | - isindir.github.com 35 | resources: 36 | - sopssecrets/finalizers 37 | verbs: 38 | - update 39 | - apiGroups: 40 | - isindir.github.com 41 | resources: 42 | - sopssecrets/status 43 | verbs: 44 | - get 45 | - patch 46 | - update 47 | -------------------------------------------------------------------------------- /config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: manager-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: manager-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: controller-manager 12 | namespace: system 13 | -------------------------------------------------------------------------------- /config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | name: controller-manager 5 | namespace: system 6 | -------------------------------------------------------------------------------- /config/rbac/sopssecret_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit sopssecrets. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: sopssecret-editor-role 6 | rules: 7 | - apiGroups: 8 | - isindir.github.com 9 | resources: 10 | - sopssecrets 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - patch 17 | - update 18 | - watch 19 | - apiGroups: 20 | - isindir.github.com 21 | resources: 22 | - sopssecrets/status 23 | verbs: 24 | - get 25 | -------------------------------------------------------------------------------- /config/rbac/sopssecret_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view sopssecrets. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: sopssecret-viewer-role 6 | rules: 7 | - apiGroups: 8 | - isindir.github.com 9 | resources: 10 | - sopssecrets 11 | verbs: 12 | - get 13 | - list 14 | - watch 15 | - apiGroups: 16 | - isindir.github.com 17 | resources: 18 | - sopssecrets/status 19 | verbs: 20 | - get 21 | -------------------------------------------------------------------------------- /config/samples/isindir_v1alpha1_sopssecret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: isindir.github.com/v1alpha1 2 | kind: SopsSecret 3 | metadata: 4 | name: sopssecret-sample 5 | spec: 6 | # Add fields here 7 | foo: bar 8 | -------------------------------------------------------------------------------- /config/samples/isindir_v1alpha2_sopssecret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: isindir.github.com/v1alpha2 2 | kind: SopsSecret 3 | metadata: 4 | name: sopssecret-sample 5 | spec: 6 | # Add fields here 7 | foo: bar 8 | -------------------------------------------------------------------------------- /config/samples/isindir_v1alpha3_sopssecret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: isindir.github.com/v1alpha3 2 | kind: SopsSecret 3 | metadata: 4 | name: sopssecret-sample 5 | spec: 6 | secretTemplates: 7 | - name: my-secret-name-0 8 | labels: 9 | label0: value0 10 | labelK: valueK 11 | annotations: 12 | key0: value0 13 | keyN: valueN 14 | stringData: 15 | data-name0: data-value0 16 | data-nameL: data-valueL 17 | - name: my-secret-name-1 18 | labels: 19 | label1: value1 20 | annotations: 21 | key1: value1 22 | data: 23 | data-name1: ZGF0YS12YWx1ZTE= 24 | data-nameM: ZGF0YS12YWx1ZU0= 25 | stringData: 26 | data-name0: data-value0 27 | - name: my-secret-name-2 28 | labels: 29 | label1: value1 30 | annotations: 31 | key1: value1 32 | data: 33 | data-name1: ZGF0YS12YWx1ZTE= 34 | data-nameM: ZGF0YS12YWx1ZU0= 35 | - name: jenkins-secret 36 | labels: 37 | "jenkins.io/credentials-type": "usernamePassword" 38 | annotations: 39 | "jenkins.io/credentials-description" : "credentials from Kubernetes" 40 | stringData: 41 | username: myUsername 42 | password: 'Pa$$word' 43 | - name: docker-login 44 | type: 'kubernetes.io/dockerconfigjson' 45 | stringData: 46 | .dockerconfigjson: '{"auths":{"index.docker.io":{"username":"imyuser","password":"mypass","email":"myuser@abc.com","auth":"aW15dXNlcjpteXBhc3M="}}}' 47 | -------------------------------------------------------------------------------- /docs/age/README.md: -------------------------------------------------------------------------------- 1 | # Local testing using age 2 | 3 | ```bash 4 | rm -f qqq.key.txt 5 | export SOPS_AGE_RECIPIENTS=$( age-keygen -o qqq.key.txt 2>&1 | awk '{ print $3 }' ) 6 | export SOPS_AGE_KEY_FILE=$PWD/qqq.key.txt 7 | 8 | cat >qqq.jenkins-secrets.yaml <" 6 | - name: AWS_SECRET_ACCESS_KEY 7 | value: "" 8 | - name: AWS_DEFAULT_REGION 9 | value: eu-west-2 10 | -------------------------------------------------------------------------------- /docs/api_upgrade_example/examples/values.0.3.0.custom.yaml: -------------------------------------------------------------------------------- 1 | replicaCount: 1 2 | 3 | extraEnv: 4 | - name: AWS_SDK_LOAD_CONFIG 5 | value: "1" 6 | - name: AWS_ACCESS_KEY_ID 7 | value: "" 8 | - name: AWS_SECRET_ACCESS_KEY 9 | value: "" 10 | - name: AWS_DEFAULT_REGION 11 | value: eu-west-2 12 | -------------------------------------------------------------------------------- /docs/artifacthub-repo.yml: -------------------------------------------------------------------------------- 1 | # Artifact Hub repository metadata file 2 | repositoryID: 3f44f275-af13-4eaf-a1e1-df4528e96955 3 | owners: 4 | - name: isindir 5 | email: isindir@users.sf.net 6 | -------------------------------------------------------------------------------- /docs/dev-notes/Versioning-README.md: -------------------------------------------------------------------------------- 1 | # Development Notes 2 | 3 | ## Files to check and update when building new version 4 | 5 | * version of docker image and kubebuilder dependencies 6 | 7 | ``` 8 | README.md 9 | Makefile 10 | ``` 11 | 12 | * golang and base image 13 | 14 | ``` 15 | Dockerfile 16 | ``` 17 | 18 | * golang, tools to build and test 19 | 20 | ``` 21 | .circleci/config.yml 22 | .tool-versions 23 | Dockerfile 24 | go.mod 25 | ``` 26 | 27 | * Dependencies - libraries 28 | 29 | ``` 30 | go.mod 31 | ``` 32 | > needs `make clean; make tidy` 33 | 34 | * cluster version (same as kubectl in other files) 35 | 36 | ``` 37 | chart/helm3/sops-secrets-operator/Makefile 38 | ``` 39 | 40 | * chart version, docker image version, kube version for kubeval 41 | 42 | ``` 43 | chart/helm3/sops-secrets-operator/Makefile 44 | chart/helm3/sops-secrets-operator/Chart.yaml 45 | chart/helm3/sops-secrets-operator/values.yaml 46 | chart/helm3/sops-secrets-operator/tests/operator_test.yaml 47 | ``` 48 | 49 | * for any new `SopsSecret` api version change needs to be updated 50 | 51 | ``` 52 | PROJECT 53 | ``` 54 | 55 | ## Before final merge of the release 56 | 57 | Before final merge of the release - package helm chart: 58 | 59 | ```bash 60 | package-helm 61 | ``` 62 | -------------------------------------------------------------------------------- /docs/gpg/Makefile: -------------------------------------------------------------------------------- 1 | ##@ General 2 | 3 | all: clean ## Generates GPG secrets (NOTE: this is not production grade configuration) 4 | ./generate-secrets.sh 5 | 6 | help: ## Display this help. 7 | @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) 8 | 9 | .PHONY: clean 10 | clean: ## Performs cleanup 11 | @rm -fr /tmp/tmp* {1,2}.yaml keys keys.tar.gz 12 | -------------------------------------------------------------------------------- /docs/gpg/README.md: -------------------------------------------------------------------------------- 1 | # Preparing GPG keys 2 | 3 | This procedure describes basic setup to use PGP keys with sops-secrets-operator. 4 | 5 | ## Create PGP keys 6 | 7 | Run docker container in the directory of this README file: 8 | 9 | ```bash 10 | docker run --rm -v $( pwd ):/tmp/scripts -ti ubuntu:20.04 bash 11 | ``` 12 | 13 | Then generate PGP keys inside container. PGP key files will remain in the folder 14 | after closing container session: 15 | 16 | ```bash 17 | cd /tmp/scripts 18 | ./install.sh 19 | make 20 | ``` 21 | 22 | Following files will be generated: 23 | 24 | * `keys.tar.gz` - GPG configuration, which can be used to encrypt/decrypt 25 | secrets, however the better approach is to use user keys to encrypt secrets, 26 | allowing these keys to decrypt secrets within cluster. 27 | * `1.yaml` and `2.yaml` - these files should be applied to the namespace where 28 | `sops-secrets-operator` will be deployed via helm chart. 29 | 30 | Sourcing `keys-env` sets up working environment for data encryption: 31 | 32 | ```bash 33 | source ./keys-env 34 | ``` 35 | 36 | After sourcing sops can be used to encrypt data, for example: 37 | 38 | ```bash 39 | sops -e -p $FP --encrypted-suffix='Templates' ../../config/samples/isindir_v1alpha3_sopssecret.yaml > example-secrets.enc.yaml 40 | ``` 41 | 42 | Then `example-secrets.enc.yaml` can be applied to the cluster to create secrets using 43 | sops CR. Resulting `keys.tar.gz`, `1.yaml` and `2.yaml` files should be kept secret 44 | itself. 45 | -------------------------------------------------------------------------------- /docs/gpg/generate-secrets.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export GNUPGHOME="$( mktemp -d )" 4 | 5 | cat >${GNUPGHOME}/foo < 1.yaml 29 | kubectl create secret generic gpg2 --from-file=${GNUPGHOME}/private-keys-v1.d -o yaml --dry-run > 2.yaml 30 | 31 | mv ${GNUPGHOME} keys 32 | tar -czvf keys.tar.gz keys 33 | rm -fr keys 34 | -------------------------------------------------------------------------------- /docs/gpg/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | apt-get update 4 | apt install -y tree curl vim gnupg2 make 5 | cd /usr/local/bin 6 | curl -LO https://storage.googleapis.com/kubernetes-release/release/`curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt`/bin/linux/amd64/kubectl 7 | chmod +x kubectl 8 | -------------------------------------------------------------------------------- /docs/gpg/keys-env: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ -f keys.tar.gz ]]; then 4 | tar -xzvf keys.tar.gz 5 | export GNUPGHOME="$( pwd )/keys" 6 | export GPG_TTY=$(tty) 7 | fi 8 | 9 | export FP=$( gpg2 --with-colons --fingerprint | awk -F: '$1 == "fpr" {print $10; exit}' ) 10 | -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.1.10.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.1.10.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.10.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.10.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.10.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.10.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.10.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.10.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.10.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.10.3.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.10.4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.10.4.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.10.5.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.10.5.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.10.6.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.10.6.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.10.7.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.10.7.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.10.8.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.10.8.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.11.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.11.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.11.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.11.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.11.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.11.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.11.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.11.3.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.12.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.12.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.12.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.12.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.12.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.12.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.12.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.12.3.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.12.4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.12.4.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.12.5.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.12.5.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.13.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.13.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.13.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.13.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.13.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.13.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.14.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.14.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.14.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.14.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.14.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.14.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.15.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.15.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.15.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.15.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.15.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.15.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.15.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.15.3.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.15.4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.15.4.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.15.5.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.15.5.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.16.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.16.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.17.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.17.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.17.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.17.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.17.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.17.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.17.4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.17.4.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.18.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.18.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.18.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.18.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.18.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.18.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.18.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.18.3.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.18.4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.18.4.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.18.5.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.18.5.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.18.6.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.18.6.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.19.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.19.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.19.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.19.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.19.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.19.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.19.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.19.3.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.19.4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.19.4.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.2.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.2.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.20.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.20.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.20.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.20.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.20.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.20.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.20.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.20.3.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.20.4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.20.4.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.20.5.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.20.5.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.21.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.21.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.22.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.22.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.3.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.3.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.3.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.3.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.3.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.3.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.3.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.3.3.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.3.4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.3.4.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.3.5.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.3.5.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.3.6.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.3.6.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.3.7.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.3.7.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.4.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.4.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.4.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.4.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.4.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.4.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.4.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.4.3.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.4.4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.4.4.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.4.5.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.4.5.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.4.6.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.4.6.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.4.7.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.4.7.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.4.8.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.4.8.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.5.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.5.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.5.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.5.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.5.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.5.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.5.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.5.3.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.6.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.6.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.6.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.6.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.6.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.6.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.6.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.6.3.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.6.4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.6.4.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.6.5.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.6.5.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.6.6.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.6.6.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.6.7.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.6.7.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.6.8.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.6.8.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.7.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.7.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.7.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.7.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.7.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.7.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.7.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.7.3.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.7.4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.7.4.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.7.5.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.7.5.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.7.6.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.7.6.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.8.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.8.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.8.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.8.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.8.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.8.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.8.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.8.3.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.8.4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.8.4.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.9.0.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.9.0.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.9.1.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.9.1.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.9.2.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.9.2.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.9.3.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.9.3.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.9.4.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.9.4.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.9.5.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.9.5.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.9.6.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.9.6.tgz -------------------------------------------------------------------------------- /docs/sops-secrets-operator-0.9.7.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/isindir/sops-secrets-operator/147febf336f14bb2546eec020680ce1b2a2e96f1/docs/sops-secrets-operator-0.9.7.tgz -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/isindir/sops-secrets-operator 2 | 3 | // UPDATE_HERE 4 | go 1.24.3 5 | 6 | require ( 7 | // https://github.com/mozilla/sops/releases 8 | github.com/getsops/sops/v3 v3.10.2 9 | // https://github.com/go-logr/logr/releases 10 | github.com/go-logr/logr v1.4.2 11 | // https://github.com/onsi/ginkgo/releases 12 | github.com/onsi/ginkgo/v2 v2.23.4 13 | // https://github.com/onsi/gomega/releases 14 | github.com/onsi/gomega v1.37.0 15 | // https://github.com/prometheus/client_golang/releases 16 | github.com/prometheus/client_golang v1.22.0 17 | // https://github.com/sirupsen/logrus/releases 18 | github.com/sirupsen/logrus v1.9.3 19 | // https://github.com/kubernetes/apimachinery/tags 20 | k8s.io/api v0.33.0 21 | k8s.io/apimachinery v0.33.0 22 | k8s.io/client-go v0.33.0 23 | // https://github.com/kubernetes-sigs/controller-runtime/releases 24 | sigs.k8s.io/controller-runtime v0.20.4 25 | ) 26 | 27 | require ( 28 | cel.dev/expr v0.22.1 // indirect 29 | cloud.google.com/go v0.120.0 // indirect 30 | cloud.google.com/go/auth v0.15.0 // indirect 31 | cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect 32 | cloud.google.com/go/compute/metadata v0.6.0 // indirect 33 | cloud.google.com/go/iam v1.4.2 // indirect 34 | cloud.google.com/go/kms v1.21.1 // indirect 35 | cloud.google.com/go/longrunning v0.6.6 // indirect 36 | cloud.google.com/go/monitoring v1.24.1 // indirect 37 | cloud.google.com/go/storage v1.51.0 // indirect 38 | filippo.io/age v1.2.1 // indirect 39 | filippo.io/edwards25519 v1.1.0 // indirect 40 | github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect 41 | github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 // indirect 42 | github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect 43 | github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 // indirect 44 | github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 // indirect 45 | github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect 46 | github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0 // indirect 47 | github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.51.0 // indirect 48 | github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.51.0 // indirect 49 | github.com/ProtonMail/go-crypto v1.2.0 // indirect 50 | github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect 51 | github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.10 // indirect 52 | github.com/aws/aws-sdk-go-v2/config v1.29.14 // indirect 53 | github.com/aws/aws-sdk-go-v2/credentials v1.17.67 // indirect 54 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect 55 | github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.72 // indirect 56 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect 57 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect 58 | github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect 59 | github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.34 // indirect 60 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect 61 | github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.0 // indirect 62 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect 63 | github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.15 // indirect 64 | github.com/aws/aws-sdk-go-v2/service/kms v1.38.3 // indirect 65 | github.com/aws/aws-sdk-go-v2/service/s3 v1.79.2 // indirect 66 | github.com/aws/aws-sdk-go-v2/service/sso v1.25.3 // indirect 67 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.1 // indirect 68 | github.com/aws/aws-sdk-go-v2/service/sts v1.33.19 // indirect 69 | github.com/aws/smithy-go v1.22.3 // indirect 70 | github.com/beorn7/perks v1.0.1 // indirect 71 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect 72 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 73 | github.com/cloudflare/circl v1.6.0 // indirect 74 | github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f // indirect 75 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 76 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect 77 | github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect 78 | github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect 79 | github.com/evanphx/json-patch/v5 v5.9.11 // indirect 80 | github.com/fatih/color v1.18.0 // indirect 81 | github.com/felixge/httpsnoop v1.0.4 // indirect 82 | github.com/fsnotify/fsnotify v1.7.0 // indirect 83 | github.com/fxamacker/cbor/v2 v2.7.0 // indirect 84 | github.com/getsops/gopgagent v0.0.0-20241224165529-7044f28e491e // indirect 85 | github.com/go-jose/go-jose/v4 v4.0.5 // indirect 86 | github.com/go-logr/stdr v1.2.2 // indirect 87 | github.com/go-logr/zapr v1.3.0 // indirect 88 | github.com/go-openapi/jsonpointer v0.21.0 // indirect 89 | github.com/go-openapi/jsonreference v0.20.2 // indirect 90 | github.com/go-openapi/swag v0.23.0 // indirect 91 | github.com/go-task/slim-sprig/v3 v3.0.0 // indirect 92 | github.com/gogo/protobuf v1.3.2 // indirect 93 | github.com/golang-jwt/jwt/v5 v5.2.2 // indirect 94 | github.com/google/btree v1.1.3 // indirect 95 | github.com/google/gnostic-models v0.6.9 // indirect 96 | github.com/google/go-cmp v0.7.0 // indirect 97 | github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect 98 | github.com/google/s2a-go v0.1.9 // indirect 99 | github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect 100 | github.com/google/uuid v1.6.0 // indirect 101 | github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect 102 | github.com/googleapis/gax-go/v2 v2.14.1 // indirect 103 | github.com/goware/prefixer v0.0.0-20160118172347-395022866408 // indirect 104 | github.com/hashicorp/errwrap v1.1.0 // indirect 105 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 106 | github.com/hashicorp/go-multierror v1.1.1 // indirect 107 | github.com/hashicorp/go-retryablehttp v0.7.7 // indirect 108 | github.com/hashicorp/go-rootcerts v1.0.2 // indirect 109 | github.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 // indirect 110 | github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect 111 | github.com/hashicorp/go-sockaddr v1.0.7 // indirect 112 | github.com/hashicorp/hcl v1.0.0 // indirect 113 | github.com/hashicorp/vault/api v1.16.0 // indirect 114 | github.com/josharian/intern v1.0.0 // indirect 115 | github.com/json-iterator/go v1.1.12 // indirect 116 | github.com/kylelemons/godebug v1.1.0 // indirect 117 | github.com/lib/pq v1.10.9 // indirect 118 | github.com/mailru/easyjson v0.7.7 // indirect 119 | github.com/mattn/go-colorable v0.1.14 // indirect 120 | github.com/mattn/go-isatty v0.0.20 // indirect 121 | github.com/mitchellh/go-homedir v1.1.0 // indirect 122 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect 123 | github.com/mitchellh/mapstructure v1.5.0 // indirect 124 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 125 | github.com/modern-go/reflect2 v1.0.2 // indirect 126 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 127 | github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect 128 | github.com/pkg/errors v0.9.1 // indirect 129 | github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect 130 | github.com/prometheus/client_model v0.6.1 // indirect 131 | github.com/prometheus/common v0.62.0 // indirect 132 | github.com/prometheus/procfs v0.15.1 // indirect 133 | github.com/ryanuber/go-glob v1.0.0 // indirect 134 | github.com/spf13/pflag v1.0.5 // indirect 135 | github.com/x448/float16 v0.8.4 // indirect 136 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 137 | go.opentelemetry.io/contrib/detectors/gcp v1.35.0 // indirect 138 | go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect 139 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect 140 | go.opentelemetry.io/otel v1.35.0 // indirect 141 | go.opentelemetry.io/otel/metric v1.35.0 // indirect 142 | go.opentelemetry.io/otel/sdk v1.35.0 // indirect 143 | go.opentelemetry.io/otel/sdk/metric v1.35.0 // indirect 144 | go.opentelemetry.io/otel/trace v1.35.0 // indirect 145 | go.uber.org/automaxprocs v1.6.0 // indirect 146 | go.uber.org/multierr v1.11.0 // indirect 147 | go.uber.org/zap v1.27.0 // indirect 148 | golang.org/x/crypto v0.37.0 // indirect 149 | golang.org/x/net v0.39.0 // indirect 150 | golang.org/x/oauth2 v0.29.0 // indirect 151 | golang.org/x/sync v0.13.0 // indirect 152 | golang.org/x/sys v0.32.0 // indirect 153 | golang.org/x/term v0.31.0 // indirect 154 | golang.org/x/text v0.24.0 // indirect 155 | golang.org/x/time v0.11.0 // indirect 156 | golang.org/x/tools v0.31.0 // indirect 157 | gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect 158 | google.golang.org/api v0.228.0 // indirect 159 | google.golang.org/genproto v0.0.0-20250324211829-b45e905df463 // indirect 160 | google.golang.org/genproto/googleapis/api v0.0.0-20250324211829-b45e905df463 // indirect 161 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250324211829-b45e905df463 // indirect 162 | google.golang.org/grpc v1.71.1 // indirect 163 | google.golang.org/protobuf v1.36.6 // indirect 164 | gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect 165 | gopkg.in/inf.v0 v0.9.1 // indirect 166 | gopkg.in/yaml.v3 v3.0.1 // indirect 167 | k8s.io/apiextensions-apiserver v0.32.1 // indirect 168 | k8s.io/klog/v2 v2.130.1 // indirect 169 | k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect 170 | k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect 171 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect 172 | sigs.k8s.io/randfill v1.0.0 // indirect 173 | sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect 174 | sigs.k8s.io/yaml v1.4.0 // indirect 175 | ) 176 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 | -------------------------------------------------------------------------------- /internal/controllers/custom_metrics.go: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 | 5 | // these metrics are introduced for learning purposes, these have almost no real value 6 | 7 | package controllers 8 | 9 | import ( 10 | "github.com/prometheus/client_golang/prometheus" 11 | "sigs.k8s.io/controller-runtime/pkg/metrics" 12 | ) 13 | 14 | var ( 15 | sopsSecretsReconciliations = prometheus.NewCounter( 16 | prometheus.CounterOpts{ 17 | Name: "sopssecrets_reconcilation_successes_total", 18 | Help: "Number of SopsSecrets reconcilations", 19 | }, 20 | ) 21 | 22 | sopsSecretsReconciliationFailures = prometheus.NewCounter( 23 | prometheus.CounterOpts{ 24 | Name: "sopssecrets_reconcilation_failures_total", 25 | Help: "Number of SopsSecrets reconcolation failures", 26 | }, 27 | ) 28 | 29 | sopsSecretsReconciliationsSuspended = prometheus.NewCounter( 30 | prometheus.CounterOpts{ 31 | Name: "sopssecrets_reconcilation_suspends_total", 32 | Help: "Number of SopsSecrets reconcilations suspends", 33 | }, 34 | ) 35 | ) 36 | 37 | func init() { 38 | // Register custom metrics with the global prometheus registry 39 | metrics.Registry.MustRegister( 40 | sopsSecretsReconciliations, 41 | sopsSecretsReconciliationFailures, 42 | sopsSecretsReconciliationsSuspended, 43 | ) 44 | } 45 | -------------------------------------------------------------------------------- /internal/controllers/custom_predicates.go: -------------------------------------------------------------------------------- 1 | package controllers 2 | 3 | import ( 4 | "reflect" 5 | 6 | corev1 "k8s.io/api/core/v1" 7 | "sigs.k8s.io/controller-runtime/pkg/event" 8 | "sigs.k8s.io/controller-runtime/pkg/predicate" 9 | ) 10 | 11 | type SecretDataTypeChangedPredicate struct { 12 | predicate.Funcs 13 | } 14 | 15 | func (d SecretDataTypeChangedPredicate) Update(e event.UpdateEvent) bool { 16 | oldSecret, oldOK := e.ObjectOld.(*corev1.Secret) 17 | newSecret, newOK := e.ObjectNew.(*corev1.Secret) 18 | 19 | if !oldOK && !newOK { 20 | return false 21 | } 22 | 23 | if !reflect.DeepEqual(oldSecret.Data, newSecret.Data) { 24 | return true 25 | } 26 | 27 | if !reflect.DeepEqual(oldSecret.StringData, newSecret.StringData) { 28 | return true 29 | } 30 | 31 | if oldSecret.Type != newSecret.Type { 32 | return true 33 | } 34 | 35 | return false 36 | } 37 | -------------------------------------------------------------------------------- /internal/controllers/suite_test.go: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 | 5 | package controllers 6 | 7 | import ( 8 | "context" 9 | "path/filepath" 10 | "testing" 11 | 12 | . "github.com/onsi/ginkgo/v2" 13 | . "github.com/onsi/gomega" 14 | "k8s.io/client-go/kubernetes" 15 | "k8s.io/client-go/kubernetes/scheme" 16 | ctrl "sigs.k8s.io/controller-runtime" 17 | "sigs.k8s.io/controller-runtime/pkg/client" 18 | "sigs.k8s.io/controller-runtime/pkg/envtest" 19 | logf "sigs.k8s.io/controller-runtime/pkg/log" 20 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 21 | 22 | isindirv1alpha3 "github.com/isindir/sops-secrets-operator/api/v1alpha3" 23 | //+kubebuilder:scaffold:imports 24 | ) 25 | 26 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to 27 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. 28 | 29 | // cfg *rest.Config 30 | var ( 31 | K8sClient client.Client 32 | testEnv *envtest.Environment 33 | ctx context.Context 34 | cancel context.CancelFunc 35 | ) 36 | 37 | func TestAPIs(t *testing.T) { 38 | RegisterFailHandler(Fail) 39 | RunSpecs(t, "Controller Suite") 40 | } 41 | 42 | var _ = BeforeSuite(func() { 43 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) 44 | 45 | ctx, cancel = context.WithCancel(ctrl.SetupSignalHandler()) 46 | 47 | By("bootstrapping test environment") 48 | 49 | testEnv = &envtest.Environment{ 50 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, 51 | ErrorIfCRDPathMissing: true, 52 | } 53 | 54 | cfg, err := testEnv.Start() 55 | Expect(err).NotTo(HaveOccurred()) 56 | Expect(cfg).NotTo(BeNil()) 57 | 58 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) 59 | 60 | err = isindirv1alpha3.AddToScheme(scheme.Scheme) 61 | Expect(err).NotTo(HaveOccurred()) 62 | 63 | //+kubebuilder:scaffold:scheme 64 | 65 | K8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 66 | Expect(err).NotTo(HaveOccurred()) 67 | Expect(K8sClient).NotTo(BeNil()) 68 | 69 | K8sClientset, err := kubernetes.NewForConfig(cfg) 70 | Expect(err).NotTo(HaveOccurred()) 71 | Expect(K8sClientset).NotTo(BeNil()) 72 | 73 | k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ 74 | Scheme: scheme.Scheme, 75 | }) 76 | Expect(err).ToNot(HaveOccurred()) 77 | Expect(k8sManager).NotTo(BeNil()) 78 | 79 | err = (&SopsSecretReconciler{ 80 | Client: k8sManager.GetClient(), 81 | Scheme: k8sManager.GetScheme(), 82 | Log: ctrl.Log.WithName("controllers").WithName("SopsSecret"), 83 | }).SetupWithManager(k8sManager) 84 | Expect(err).ToNot(HaveOccurred()) 85 | 86 | go func() { 87 | defer GinkgoRecover() 88 | err = k8sManager.Start(ctx) 89 | Expect(err).ToNot(HaveOccurred()) 90 | }() 91 | }) 92 | 93 | var _ = AfterSuite(func() { 94 | cancel() 95 | By("tearing down the test environment") 96 | err := testEnv.Stop() 97 | Expect(err).NotTo(HaveOccurred()) 98 | }) 99 | --------------------------------------------------------------------------------